Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all 5930 articles
Browse latest View live

Android的Service使用

$
0
0

Service

Service属于Android的四大组件(只要是安卓四大组件就必须在AndroidMainFest中进行注册才可以使用),Service运行不赖于界面,即使退出应用,Service也将运行在后台中。服务在创建后,默认运行在主线程中,所以我们需要自己去开辟子线程,如果不开辟就有可能出现主线程被阻塞。

Android多线程

对于很多耗时操作(比如:发起网络请求,由于网络很慢,服务器不一定立刻响应),我们一般把操作放在子线程中进行。

线程基本用法

  • 定义一个线程需要继承Thread类,重写run()方法:
  class XXThread extends Thread {
    @Override
    public void run() {
        // 处理具体的逻辑
    }
}  

启动该线程:

    new XXThread().start();
  • 使用Runable接口方法来定义一个线程
    class XXThread implements Runnable{

        @Override
        public void run() {

        }
}

启动该线程

    XXThread mThread = new XXThread();
    new Thread(mThread).start();
  • 使用匿名类的方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 处理具体的逻辑
            }
        }).start();

异步消息处理机制

我们是不能在子线程中更新UI中的内容,但是有时候我们却需要把子线程的内容更新到UI,Android提供一套异步消息处理机制来解决这个问题。

从图中可以发现,异步消息处理有4部分,分别是Message,Handle,MessageQuery,Looper四部分组成。在线程中我们调用 hanler.setMessage() 方法来发送消息给 MessageQuery,然后通过 Looper 来取出带处理消息,并且传回给 handler,然后调用在 UI 中的 handler.handleMessage() 方法来实现对 UI中的内容更新。

代码示例

TEXT_UPDATE自己定义的一个常量

  • UI线程中的代码
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case TEXT_UPDATE:
                    tvShow.setText("你改变了TextView的内容");
                    break;
                default:
                    break;
            }
        }
    };
  • 子线程中的代码
    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.btnUpdate:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = TEXT_UPDATE;
                        handler.sendMessage(message);
                    }
                }).start();
                break;
            default:
                break;
        }
    }

Service应用

前面讨论这么多关于多线程编程的问题都为在Service的使用上做准备的。先讨论最简单的基本用法

基本用法

Service和Activity一样都拥有 onCreate()onDestroy()方法,来实现Service的创建和销毁。当然他们也有区别,在Service中我们必须实现 onBind() 方法,该方法用于与UI绑定(先不讨论)。在Service还有一个方法叫 onStartCommand(),这个方法和 onCreate() 区别是:onStartCommand()每次Service启动(包括第一次创建)都会启动这个方法,但是onCreate()只会在第一次创建的时候启动,之后不会再启动该方法

代码示例

  • 创建一个Service
public class TestService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
}
  • 启动一个Service
    Intent startIntent = new Intent(this, TestService.class);
    startService(startIntent);
  • 停止一个Service
    停止Service需要注意,如果Service不仅被创建了,而且被绑定了,那么如果要停止这个Service,就需要同时执行 stopService()unbindService(),才可以停止Service。
    Intent stopIntent = new Intent(this, TestService.class);
    stopService(stopIntent);

将Service绑定到UI中

如果要将Service绑定到UI中去,必须在 onBind() 方法中传回一个对象,使得UI可以操作Service中的方法,这个对象就好比一座桥梁连接着UI和Service。

代码示例

  • 创建一个Biner对象,来实现Service中的公共方法
   private DownloadBinder mBinder = new DownloadBinder();

    class DownloadBinder extends Binder {

        public void startDownload() {
            Log.e(TAG, "开始下载");
        }

        public int getProgress() {
            Log.e(TAG, "获取进度表");
            return 0;
        }

        public void stopDownload() {
            Log.e(TAG, "停止下载");
        }
    }
  • 桥梁的建立
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回一个连接通道给service
        return mBinder;
    }

到此UI还没有和Service建立真正的桥梁,在UI中必须调用 bindService() 方法来绑定服务,在使用此方法之前,我们还需要 ServiceConnection的实现,来建立UI与Ser的连接。ServiceConnection 中有两个方法需要实现分别是 onServiceConnected()onServiceDisconnected(),前者在调用bindService() 来建立连接的时候,会去调用此方法,但是在解除绑定的时候,却不会调用onServiceDisconnected(),只要发生系统意外的时候,才会调用此方法。我们在onServiceConnected()中来调用Service中的公共方法

  • ServiceConnection的实现
    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            downloadBinder = (TestService.DownloadBinder) service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //系统发生意外中断的时候才会调用此方法
            //客户端取消绑定的时候不会调用
        }
    };

到此我们还是不能调用Service中的方法,因为我们还没有使用onServiceConnected()来绑定。BIND_AUTO_CREATE 代表自动完成绑定。

  • 桥梁的完成
    Intent bindIntent = new Intent(this, TestService.class);
    bindService(bindIntent, connection, BIND_AUTO_CREATE);

到此我们就可以任意调用Service中的公共方法了

  • 桥梁的爆破
    解除绑定是非常简单的,只需要调用 unbindService() 方法,需要注意的是:如果已经解除绑定了,再次调用会报错
    unbindService(connection);

到这我们就已经学习了Service的创建、删除、绑定、取消绑定。但是这些都是在UI主线程中进行,就像我们前面提到如果遇到了耗时任务,将会被阻塞。所以我们要使用多线程技术。

IntendService的应用

IntendService的使用,可以使得开发者不必去停止服务,系统会在请求结束后自动停止服务。我们只需要在 onHandleIntent() 来完成UI所提出的任务即可,不过我们还需要为IntendService提供一个构造函数

代码示例

public class IntentServiceDemo extends IntentService {
    private static final String TAG_INTENT_SERVICE = "IntentService";

    public IntentServiceDemo() {
        super("IntentServiceDemo");
    }


    @Override
    protected void onHandleIntent(Intent intent) {
        Log.e(TAG_INTENT_SERVICE, "线程id是:" + Thread.currentThread().getId());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG_INTENT_SERVICE,"服务已经被销毁");
    }
}

启动IntendService

     Log.e("MainActivity", "线程id:" + Thread.currentThread().getId());
     Intent intentService = new Intent(this, IntentServiceDemo.class);
     startService(intentService);

这样Service就运行在子线程中。当然我们也可以不使用IntendService来实现,我们可以自己在 onStartCommand() 方法中,开启一个子线程。但是这样开启一个子线程将永远在执行,所以我们必须在任务完成后,调用 stopSelf() 或者 stopService()方法来关闭。

代码示例

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        new Thread(new Runnable() {
            @Override
            public void run() {   
               Log.e(TAG,"每次服务被创建");
                stopSelf();
            }
        }).start();

        return super.onStartCommand(intent, flags, startId);
    }

前台Service

应用场景,如果一个天气app,当后台数据更新后,会一直在前台显示更新后的数据情况。这就可以使用前台Service来实现。要在前台一直显示数据,这有点类似于Notification,当时我们不使用NotificationManger来显示出来,而是使用 startForeground()来实现。要注意 startForeground() 方法中的第一个参数id,不能使用0作为id。

代码示例

    @Override
    public void onCreate() {
        super.onCreate();
        //  Log.e(TAG, "服务被创建");
        //前台服务
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);

        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

        builder.setContentTitle("通知");
        builder.setContentText("吃饭了么");
        builder.setContentIntent(pendingIntent);
        builder.setSmallIcon(R.drawable.ic_launcher);
        builder.setDefaults(Notification.DEFAULT_ALL);
        builder.setAutoCancel(true);

        startForeground(1, builder.build());
    }

后台定时执行任务

通过AlarmManager(在这不多说)来实现定时功能,同样首先我们通过 getSystemService()方法获得系统服务。然后通过set() 方法来定时唤醒 PendingIntent.getBroadcast()* 方法来发送一个广播,然后再广播内启动Service。整个流程如下图

代码示例

  • Service
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "执行时间:" + new Date().toString());
            }
        }).start();

        AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
        int aSecond = 60 * 1000;//1分钟
        long triggerAtTime = SystemClock.elapsedRealtime() + aSecond;
        Intent i = new Intent(this, AlarmReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, i, 0);
        manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
        //Log.e(TAG,"每次服务被创建");
        return super.onStartCommand(intent, flags, startId);
    }
  • Broadcoast
public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("AlarmReceiver", "接收到了");
        Intent i = new Intent(context, ServiceDemo.class);
        context.startService(i);
    }
}

最后还是感谢《第一行代码》的作者,郭神,文中很多都是来自于这本书。

作者:u011665459 发表于2016/8/13 21:17:36 原文链接
阅读:142 评论:1 查看评论

Android中常用146种颜色对应的xml色值文件

$
0
0
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="white">#FFFFFF</color> <!--白色 -->
    <color name="ivory">#FFFFF0</color> <!--象牙色 -->
    <color name="lightyellow">#FFFFE0</color> <!--亮黄色 -->
    <color name="yellow">#FFFF00</color> <!--黄色 -->
    <color name="snow">#FFFAFA</color> <!--雪白色 -->
    <color name="floralwhite">#FFFAF0</color> <!--花白色 -->
    <color name="lemonchiffon">#FFFACD</color> <!--柠檬绸色 -->
    <color name="cornsilk">#FFF8DC</color> <!--米绸色 -->
    <color name="seashell">#FFF5EE</color> <!--海贝色 -->
    <color name="lavenderblush">#FFF0F5</color> <!--淡紫红 -->
    <color name="papayawhip">#FFEFD5</color> <!--番木色 -->
    <color name="blanchedalmond">#FFEBCD</color> <!--白杏色 -->
    <color name="mistyrose">#FFE4E1</color> <!--浅玫瑰色 -->
    <color name="bisque">#FFE4C4</color> <!--桔黄色 -->
    <color name="moccasin">#FFE4B5</color> <!--鹿皮色 -->
    <color name="navajowhite">#FFDEAD</color> <!--纳瓦白 -->
    <color name="peachpuff">#FFDAB9</color> <!--桃色 -->
    <color name="gold">#FFD700</color> <!--金色 -->
    <color name="pink">#FFC0CB</color> <!--粉红色 -->
    <color name="lightpink">#FFB6C1</color> <!--亮粉红色 -->
    <color name="orange">#FFA500</color> <!--橙色 -->
    <color name="lightsalmon">#FFA07A</color> <!--亮肉色 -->
    <color name="darkorange">#FF8C00</color> <!--暗桔黄色 -->
    <color name="coral">#FF7F50</color> <!--珊瑚色 -->
    <color name="hotpink">#FF69B4</color> <!--热粉红色 -->
    <color name="tomato">#FF6347</color> <!--西红柿色 -->
    <color name="orangered">#FF4500</color> <!--红橙色 -->
    <color name="deeppink">#FF1493</color> <!--深粉红色 -->
    <color name="fuchsia">#FF00FF</color> <!--紫红色 -->
    <color name="magenta">#FF00FF</color> <!--红紫色 -->
    <color name="red">#FF0000</color> <!--红色 -->
    <color name="oldlace">#FDF5E6</color> <!--老花色 -->
    <color name="lightgoldenrodyellow">#FAFAD2</color> <!--亮金黄色 -->
    <color name="linen">#FAF0E6</color> <!--亚麻色 -->
    <color name="antiquewhite">#FAEBD7</color> <!--古董白 -->
    <color name="salmon">#FA8072</color> <!--鲜肉色 -->
    <color name="ghostwhite">#F8F8FF</color> <!--幽灵白 -->
    <color name="mintcream">#F5FFFA</color> <!--薄荷色 -->
    <color name="whitesmoke">#F5F5F5</color> <!--烟白色 -->
    <color name="beige">#F5F5DC</color> <!--米色 -->
    <color name="wheat">#F5DEB3</color> <!--浅黄色 -->
    <color name="sandybrown">#F4A460</color> <!--沙褐色 -->
    <color name="azure">#F0FFFF</color> <!--天蓝色 -->
    <color name="honeydew">#F0FFF0</color> <!--蜜色 -->
    <color name="aliceblue">#F0F8FF</color> <!--艾利斯兰 -->
    <color name="khaki">#F0E68C</color> <!--黄褐色 -->
    <color name="lightcoral">#F08080</color> <!--亮珊瑚色 -->
    <color name="palegoldenrod">#EEE8AA</color> <!--苍麒麟色 -->
    <color name="violet">#EE82EE</color> <!--紫罗兰色 -->
    <color name="darksalmon">#E9967A</color> <!--暗肉色 -->
    <color name="lavender">#E6E6FA</color> <!--淡紫色 -->
    <color name="lightcyan">#E0FFFF</color> <!--亮青色 -->
    <color name="burlywood">#DEB887</color> <!--实木色 -->
    <color name="plum">#DDA0DD</color> <!--洋李色 -->
    <color name="gainsboro">#DCDCDC</color> <!--淡灰色 -->
    <color name="crimson">#DC143C</color> <!--暗深红色 -->
    <color name="palevioletred">#DB7093</color> <!--苍紫罗兰色 -->
    <color name="goldenrod">#DAA520</color> <!--金麒麟色 -->
    <color name="orchid">#DA70D6</color> <!--淡紫色 -->
    <color name="thistle">#D8BFD8</color> <!--蓟色 -->
    <color name="lightgray">#D3D3D3</color> <!--亮灰色 -->
    <color name="lightgrey">#D3D3D3</color> <!--亮灰色 -->
    <color name="tan">#D2B48C</color> <!--茶色 -->
    <color name="chocolate">#D2691E</color> <!--巧可力色 -->
    <color name="peru">#CD853F</color> <!--秘鲁色 -->
    <color name="indianred">#CD5C5C</color> <!--印第安红 -->
    <color name="mediumvioletred">#C71585</color> <!--中紫罗兰色 -->
    <color name="silver">#C0C0C0</color> <!--银色 -->
    <color name="darkkhaki">#BDB76B</color> <!--暗黄褐色-->
    <color name="rosybrown">#BC8F8F</color> <!--褐玫瑰红 -->
    <color name="mediumorchid">#BA55D3</color> <!--中粉紫色 -->
    <color name="darkgoldenrod">#B8860B</color> <!--暗金黄色 -->
    <color name="firebrick">#B22222</color> <!--火砖色 -->
    <color name="powderblue">#B0E0E6</color> <!--粉蓝色 -->
    <color name="lightsteelblue">#B0C4DE</color> <!--亮钢兰色-->
    <color name="paleturquoise">#AFEEEE</color> <!--苍宝石绿 -->
    <color name="greenyellow">#ADFF2F</color> <!--黄绿色 -->
    <color name="lightblue">#ADD8E6</color> <!--亮蓝色 -->
    <color name="darkgray">#A9A9A9</color> <!--暗灰色 -->
    <color name="darkgrey">#A9A9A9</color> <!--暗灰色 -->
    <color name="brown">#A52A2A</color> <!--褐色 -->
    <color name="sienna">#A0522D</color> <!--赭色 -->
    <color name="darkorchid">#9932CC</color> <!--暗紫色 -->
    <color name="palegreen">#98FB98</color> <!--苍绿色 -->
    <color name="darkviolet">#9400D3</color> <!--暗紫罗兰色 -->
    <color name="mediumpurple">#9370DB</color> <!--中紫色 -->
    <color name="lightgreen">#90EE90</color> <!--亮绿色 -->
    <color name="darkseagreen">#8FBC8F</color> <!--暗海兰色 -->
    <color name="saddlebrown">#8B4513</color> <!--重褐色 -->
    <color name="darkmagenta">#8B008B</color> <!--暗洋红 -->
    <color name="darkred">#8B0000</color> <!--暗红色 -->
    <color name="blueviolet">#8A2BE2</color> <!--紫罗兰蓝色 -->
    <color name="lightskyblue">#87CEFA</color> <!--亮天蓝色 -->
    <color name="skyblue">#87CEEB</color> <!--天蓝色 -->
    <color name="gray">#808080</color> <!--灰色 -->
    <color name="grey">#808080</color> <!--灰色 -->
    <color name="olive">#808000</color> <!--橄榄色 -->
    <color name="purple">#800080</color> <!--紫色 -->
    <color name="maroon">#800000</color> <!--粟色 -->
    <color name="aquamarine">#7FFFD4</color> <!--碧绿色 -->
    <color name="chartreuse">#7FFF00</color> <!--黄绿色 -->
    <color name="lawngreen">#7CFC00</color> <!--草绿色 -->
    <color name="mediumslateblue">#7B68EE</color> <!--中暗蓝色 -->
    <color name="lightslategray">#778899</color> <!--亮蓝灰 -->
    <color name="lightslategrey">#778899</color> <!--亮蓝灰 -->
    <color name="slategray">#708090</color> <!--灰石色 -->
    <color name="slategrey">#708090</color> <!--灰石色 -->
    <color name="olivedrab">#6B8E23</color> <!--深绿褐色 -->
    <color name="slateblue">#6A5ACD</color> <!--石蓝色 -->
    <color name="dimgray">#696969</color> <!--暗灰色 -->
    <color name="dimgrey">#696969</color> <!--暗灰色 -->
    <color name="mediumaquamarine">#66CDAA</color> <!--中绿色 -->
    <color name="cornflowerblue">#6495ED</color> <!--菊兰色 -->
    <color name="cadetblue">#5F9EA0</color> <!--军兰色 -->
    <color name="darkolivegreen">#556B2F</color> <!--暗橄榄绿-->
    <color name="indigo">#4B0082</color> <!--靛青色 -->
    <color name="mediumturquoise">#48D1CC</color> <!--中绿宝石 -->
    <color name="darkslateblue">#483D8B</color> <!--暗灰蓝色 -->
    <color name="steelblue">#4682B4</color> <!--钢兰色 -->
    <color name="royalblue">#4169E1</color> <!--皇家蓝 -->
    <color name="turquoise">#40E0D0</color> <!--青绿色 -->
    <color name="mediumseagreen">#3CB371</color> <!--中海蓝 -->
    <color name="limegreen">#32CD32</color> <!--橙绿色 -->
    <color name="darkslategray">#2F4F4F</color> <!--暗瓦灰色 -->
    <color name="darkslategrey">#2F4F4F</color> <!--暗瓦灰色 -->
    <color name="seagreen">#2E8B57</color> <!--海绿色 -->
    <color name="forestgreen">#228B22</color> <!--森林绿 -->
    <color name="lightseagreen">#20B2AA</color> <!--亮海蓝色 -->
    <color name="dodgerblue">#1E90FF</color> <!--闪兰色 -->
    <color name="midnightblue">#191970</color> <!--中灰兰色 -->
    <color name="aqua">#00FFFF</color> <!--浅绿色 -->
    <color name="cyan">#00FFFF</color> <!--青色 -->
    <color name="springgreen">#00FF7F</color> <!--春绿色 -->
    <color name="lime">#00FF00</color> <!--酸橙色 -->
    <color name="mediumspringgreen">#00FA9A</color> <!--中春绿色 -->
    <color name="darkturquoise">#00CED1</color> <!--暗宝石绿 -->
    <color name="deepskyblue">#00BFFF</color> <!--深天蓝色 -->
    <color name="darkcyan">#008B8B</color> <!--暗青色 -->
    <color name="teal">#008080</color> <!--水鸭色 -->
    <color name="green">#008000</color> <!--绿色 -->
    <color name="darkgreen">#006400</color> <!--暗绿色 -->
    <color name="blue">#0000FF</color> <!--蓝色 -->
    <color name="mediumblue">#0000CD</color> <!--中兰色 -->
    <color name="darkblue">#00008B</color> <!--暗蓝色 -->
    <color name="navy">#000080</color> <!--海军色 -->
    <color name="black">#000000</color> <!--黑色 -->

</resources>
作者:RichieZhu 发表于2016/8/13 21:21:11 原文链接
阅读:148 评论:0 查看评论

0102 - Android 简介 - 应用基础知识

$
0
0

应用基础知识

来源:Android Develop - API Guides - Introduction - App Fundamental

Android 应用采用 Java 编程语言编写。Android SDK 工具将您的代码—连同任何数据和资源文件—编译到一个 APK: Android 软件包,即带有 .apk 后缀的存档文件中。一个 APK 文件包含 Android 应用的所有内容,它是基于 Android 系统的设备用来安装应用的文件。

安装到设备后,每个 Android 应用都运行在自己的安全沙箱内:

  • Android 操作系统是一种多用户 Linux 系统,其中的每个应用都是一位不同的用户;
  • 默认情况下,系统会为每个应用分配一个唯一的 Linux 用户 ID(该 ID 仅由系统使用,应用并不知晓)。系统为应用中的所有文件设置权限,使得只有分配给该应用的用户 ID 才能访问这些文件;
  • 每个进程都具有自己的虚拟机 (VM),因此应用代码是在与其他应用隔离的环境中运行;
  • 默认情况下,每个应用都在其自己的 Linux 进程内运行。Android 会在需要执行任何应用组件时启动该进程,然后在不再需要该进程或系统必须为其他应用恢复内存时关闭该进程。

Android 系统可以通过这种方式实现最小权限原则。也就是说,默认情况下,每个应用都只能访问执行其工作所需的组件,而不能访问其他组件。 这样便营造出一个非常安全的环境,在这个环境中,应用无法访问系统中其未获得权限的部分。

不过,应用仍然可以通过一些途径与其他应用共享数据以及访问系统服务:

  • 可以安排两个应用共享同一 Linux 用户 ID,在这种情况下,它们能够相互访问彼此的文件。为了节省系统资源,可以安排具有相同用户 ID 的应用在同一 Linux 进程中运行,并共享同一 VM(应用还必须使用相同的证书签署);
  • 应用可以请求访问设备数据(如用户的联系人、短信、可装入存储装置 [SD 卡]、相机、蓝牙等)的权限。所有应用权限都必须由用户在安装时授予。

以上内容阐述了有关 Android 应用在系统内存在方式的基础知识。本文的其余部分将向您介绍以下内容:

  • 定义应用的核心框架组件
  • 您用来声明组件和应用必需设备功能的清单文件
  • 与应用代码分离并允许您的应用针对各种设备配置适当优化其行为的资源

应用组件


应用组件是 Android 应用的基本构建基块。每个组件都是一个不同的点,系统可以通过它进入您的应用。并非所有组件都是用户的实际入口点,有些组件相互依赖,但每个组件都以独立实体形式存在,并发挥特定作用—每个组件都是唯一的构建基块,有助于定义应用的总体行为。

共有四种不同的应用组件类型。每种类型都服务于不同的目的,并且具有定义组件的创建和销毁方式的不同生命周期。

以下便是这四种应用组件类型:

Activity
Activity表示具有用户界面的单一屏幕。例如,电子邮件应用可能具有一个显示新电子邮件列表的 Activity、一个用于撰写电子邮件的 Activity 以及一个用于阅读电子邮件的 Activity。 尽管这些 Activity 通过协作在电子邮件应用中形成了一种具有凝聚力的用户体验,但每一个 Activity 都独立于其他 Activity 而存在。 因此,其他应用可以启动其中任何一个 Activity(如果电子邮件应用允许)。 例如,相机应用可以启动电子邮件应用内用于撰写新电子邮件的 Activity,以便用户共享图片。

Activity 作为 Activity 的子类实现,您可以在Activity开发者指南中了解有关它的更多详情。

服务
服务 是一种在后台运行的组件,用于执行长时间运行的操作或为远程进程执行作业。 服务不提供用户界面。 例如,当用户位于其他应用中时,服务可能在后台播放音乐或者通过网络获取数据,但不会阻断用户与 Activity 的交互。 诸如 Activity 等其他组件可以启动服务,让其运行或与其绑定以便与其进行交互。

服务作为 Service 的子类实现,您可以在服务开发者指南中了解有关它的更多详情。

内容提供程序
内容提供程序 管理一组共享的应用数据。您可以将数据存储在文件系统、SQLite 数据库、Web 上或您的应用可以访问的任何其他永久性存储位置。其他应用可以通过内容提供程序查询数据,甚至修改数据(如果内容提供程序允许)。 例如,Android 系统可提供管理用户联系人信息的内容提供程序。因此,任何具有适当权限的应用都可以查询内容提供程序的某一部分(如 ContactsContract.Data),以读取和写入有关特定人员的信息。

内容提供程序也适用于读取和写入您的应用不共享的私有数据。 例如,记事本示例应用使用内容提供程序来保存笔记。

内容提供程序作为 ContentProvider 的子类实现,并且必须实现让其他应用能够执行事务的一组标准 API。如需了解详细信息,请参阅内容提供程序开发者指南。

广播接收器
广播接收器 是一种用于响应系统范围广播通知的组件。 许多广播都是由系统发起的—例如,通知屏幕已关闭、电池电量不足或已拍摄照片的广播。应用也可以发起广播—例如,通知其他应用某些数据已下载至设备,并且可供其使用。 尽管广播接收器不会显示用户界面,但它们可以创建状态栏通知,在发生广播事件时提醒用户。 但广播接收器更常见的用途只是作为通向其他组件的“通道”,设计用于执行极少量的工作。 例如,它可能会基于事件发起一项服务来执行某项工作。

广播接收器作为 BroadcastReceiver 的子类实现,并且每条广播都作为 Intent 对象进行传递。如需了解详细信息,请参阅 BroadcastReceiver类。

Android 系统设计的独特之处在于,任何应用都可以启动其他应用的组件。例如,如果您想让用户使用设备的相机拍摄照片,很可能有另一个应用可以执行该操作,那么您的应用就可以利用该应用,而不是开发一个 Activity 来自行拍摄照片。 您不需要集成甚至链接到该相机应用的代码,而是只需在拍摄照片的相机应用中启动该 Activity。 完成拍摄时,系统甚至会将照片返回您的应用,以便您使用。对用户而言,就好像相机真正是您应用的组成部分。

当系统启动某个组件时,会启动该应用的进程(如果尚未运行),并实例化该组件所需的类。 例如,如果您的应用启动相机应用中拍摄照片的 Activity,则该 Activity 会在属于相机应用的进程,而不是您的应用的进程中运行。因此,与大多数其他系统上的应用不同,Android 应用并没有单一入口点(例如,没有 main() 功能)。

由于系统在单独的进程中运行每个应用,且其文件权限会限制对其他应用的访问,因此您的应用无法直接启动其他应用中的组件,但 Android 系统却可以。因此,要想启动其他应用中的组件,您必须向系统传递一则消息,说明您想启动特定组件的 Intent。 系统随后便会为您启动该组件。

启动组件

四种组件类型中的三种—Activity、服务和广播接收器—通过名为 Intent 的异步消息进行启动。 Intent 会在运行时将各个组件相互绑定(您可以将 Intent 视为从其他组件请求操作的信使),无论组件属于您的应用还是其他应用。

Intent 使用 Intent 对象创建,它定义的消息用于启动特定组件或特定类型的组件— Intent 可以是显式的,也可以是隐式的。

对于 Activity 和服务, Intent 定义要执行的操作(例如,“查看”或“发送”某个内容),并且可以指定要执行操作的数据的 URI(以及正在启动的组件可能需要了解的信息)。 例如, Intent 传达的请求可以是启动一个显示图像或打开网页的 Activity。 在某些情况下,您可以启动 Activity 来接收结果,在这种情况下,Activity 也会在 Intent 中返回结果(例如,您可以发出一个 Intent,让用户选取某位联系人并将其返回给您 — 返回 Intent 包括指向所选联系人的 URI)。

对于广播接收器, Intent 只会定义要广播的通知(例如,指示设备电池电量不足的广播只包括指示“电池电量不足”的已知操作字符串)。

Intent 不会启动另一个组件类型 - 内容提供程序,后者会在成为 ContentResolver 的请求目标时启动。内容解析程序通过内容提供程序处理所有直接事务,使得通过提供程序执行事务的组件可以无需执行事务,而是改为在 ContentResolver 对象上调用方法。这会在内容提供程序与请求信息的组件之间留出一个抽象层(以确保安全)。

每种类型的组件有不同的启动方法:

如需了解有关 Intent 用法的详细信息,请参阅 Intent 和 Intent 过滤器文档。 以下文档中还提供了有关启动特定组件的详细信息: Activity服务BroadcastReceiver 和内容提供程序

清单文件


在 Android 系统启动应用组件之前,系统必须通过读取应用的 AndroidManifest.xml 文件(“清单”文件)确认组件存在。您的应用必须在此文件中声明其所有组件,该文件必须位于应用项目目录的根目录中。

除了声明应用的组件外,清单文件还有许多其他作用,如:

  • 确定应用需要的任何用户权限,如互联网访问权限或对用户联系人的读取权限
  • 根据应用使用的 API,声明应用所需的最低API 级别
  • 声明应用使用或需要的硬件和软件功能,如相机、蓝牙服务或多点触摸屏幕
  • 应用需要链接的 API 库(Android 框架 API 除外),如 Google Maps API 库
  • 其他功能

声明组件

清单文件的主要任务是告知系统有关应用组件的信息。例如,清单文件可以想下面这样声明 Activity:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:icon="@drawable/app_icon.png" ... >
        <activity android:name="com.example.project.ExampleActivity"
                  android:label="@string/example_label" ... >
        </activity>
        ...
    </application>
</manifest>

在 <application> 元素中,android:icon 属性指向标识应用的图标所对应的资源。

在 <activity> 元素中,android:name 属性指定 Activity 子类的完全限定类名,android:label 属性指定用作 Activity 的用户可见标签的字符串。

您必须通过以下方式声明所有应用组件:

您包括在源代码中,但未在清单文件中声明的 Activity、服务和内容提供程序对系统不可见,因此也永远不会运行。 不过,广播接收器可以在清单文件中声明或在代码中动态创建(如 BroadcastReceiver 对象)并通过调用 registerReceiver() 在系统中注册。

如需了解有关如何为您的应用构建清单文件的详细信息,请参阅 AndroidManifest.xml 文件文档。

声明组件功能

如上文启动组件中所述,您可以使用 Intent 来启动 Activity、服务和广播接收器。您可以通过在 Intent 中显式命名目标组件(使用组件类名)来执行此操作。 不过,Intent 的真正强大之处在于隐式 Intent 概念。 隐式 Intent 的作用无非是描述要执行的操作类型(还可选择描述您想执行的操作所针对的数据),让系统能够在设备上找到可执行该操作的组件,并启动该组件。 如果有多个组件可以执行 Intent 所描述的操作,则由用户选择使用哪一个组件。

系统通过将接收到的 Intent 与设备上的其他应用的清单文件中提供的 Intent 过滤器进行比较来确定可以响应 Intent 的组件。

当您在应用的清单文件中声明 Activity 时,可以选择性地加入声明 Activity 功能的 Intent 过滤器,以便响应来自其他应用的 Intent。 您可以通过将&lt;intent-filter&gt; 元素作为组件声明元素的子项进行添加来为您的组件声明 Intent 过滤器。

例如,如果您开发的一个电子邮件应用包含一个用于撰写新电子邮件的 Activity,则可以像下面这样声明一个 Intent 过滤器来响应“send” Intent(以发送新电子邮件):

<manifest ... >
    ...
    <application ... >
        <activity android:name="com.example.project.ComposeEmailActivity">
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <data android:type="*/*" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
</manifest>

然后,如果另一个应用创建了一个包含 ACTION_SEND 操作的 Intent,并将其传递到 startActivity(),则系统可能会启动您的 Activity,以便用户能够草拟并发送电子邮件。

如需了解有关创建 Intent 过滤器的详细信息,请参阅 Intent 和 Intent 过滤器文档。

声明应用要求

基于 Android 系统的设备多种多样,并非所有设备都提供相同的特性和功能。为防止将您的应用安装在缺少应用所需特性的设备上,您必须通过在清单文件中声明设备和软件要求,为您的应用支持的设备类型明确定义一个配置文件。 其中的大多数声明只是为了提供信息,系统不会读取它们,但 Google Play 等外部服务会读取它们,以便当用户在其设备中搜索应用时为用户提供过滤功能。

例如,如果您的应用需要相机,并使用 Android 2.1(API 7 级)中引入的 API,您应该像下面这样在清单文件中以要求形式声明这些信息:

<manifest ... >
    <uses-feature android:name="android.hardware.camera.any"
                  android:required="true" />
    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
    ...
</manifest>

现在,没有相机且 Android 版本低于 2.1 的设备将无法从 Google Play 安装您的应用。

不过,您也可以声明您的应用使用相机,但并不要求必须使用。 在这种情况下,您的应用必须将 required 属性设置为 "false",并在运行时检查设备是否具有相机,然后根据需要禁用任何相机功能。

设备兼容性文档中提供了有关如何管理应用与不同设备兼容性的详细信息。

应用资源


Android 应用并非只包含代码—它还需要与源代码分离的资源,如图像、音频文件以及任何与应用的视觉呈现有关的内容。例如,您应该通过 XML 文件定义 Activity 用户界面的动画、菜单、样式、颜色和布局。使用应用资源能够在不修改代码的情况下轻松地更新应用的各种特性,并可通过提供备用资源集让您能够针对各种设备配置(如不同的语言和屏幕尺寸)优化您的应用。

对于您的 Android 项目中包括的每一项资源,SDK 构建工具都会定义一个唯一的整型 ID,您可以利用它来引用应用代码或 XML 中定义的其他资源中的资源。例如,如果您的应用包含一个名为 logo.png 的图像文件(保存在 res/drawable/ 目录中),则 SDK 工具会生成一个名为 R.drawable.logo 的资源 ID,您可以利用它来引用该图像并将其插入您的用户界面。

提供与源代码分离的资源的其中一个最重要优点在于,您可以提供针对不同设备配置的备用资源。 例如,通过在 XML 中定义 UI 字符串,您可以将字符串翻译为其他语言,并将这些字符串保存在单独的文件中。然后,Android 系统会根据向资源目录名称追加的语言限定符(如为法语字符串值追加res/values-fr/)和用户的语言设置,对您的 UI 应用相应的语言字符串。

Android 支持许多不同的备用资源限定符。限定符是一种加入到资源目录名称中,用来定义这些资源适用的设备配置的简短字符串。 再举一例,您应该经常会根据设备的屏幕方向和尺寸为 Activity 创建不同的布局。 例如,当设备屏幕为纵向(长型)时,您可能想要一种垂直排列按钮的布局;但当屏幕为横向(宽型)时,应按水平方向排列按钮。 要想根据方向更改布局,您可以定义两种不同的布局,然后对每个布局的目录名称应用相应的限定符。 然后,系统会根据当前设备方向自动应用相应的布局。

如需了解有关可以在应用中包括的不同资源类型以及如何针对不同设备配置创建备用资源的详细信息,请阅读提供资源



作者:qzw9231000 发表于2016/8/14 10:47:23 原文链接
阅读:71 评论:0 查看评论

Android初级教程:shape的基本用法

$
0
0

转载本文请注明出处:http://blog.csdn.net/qq_32059827/article/details/52203347   点击打开链接

在自定义进度条之前,先来学习一下shape的用法。

一、在res目录下边新建一个drawble目录(如果您会自定义状态选择器的话,这将很简单)

二、新建一个android.xml文件,找到shape,点击finish

我们可以看到shape属性并不多,截图如下:


现在逐步介绍常用的属性用法:

1、<corners />表示  “角”,表示圆角

   (1)、radius : 表示半径android:radius="5dip"代表,指定圆角半径为5dip

2、<gradient />  :表示颜色渐变。放射性改变

测试上边两个属性特征:

在drawable/shapetest.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >

    <corners android:radius="5dip" >
    </corners>

    <gradient
        android:endColor="#00ff00"
        android:startColor="#ff0000" />

</shape>

在测试activity的布局文件引入shape.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <TextView
        android:textSize="22sp"
        android:background="@drawable/shapetest"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>

此时显示效果图片:

好了。接着看shape的后边的几个属性:

3、<padding />表示内间距,与布局文件中的padding效果一样,就不做介绍。

4、<size />同上,可以直接在布局文件设置

5、<solid />固定颜色,设置这个原色后,放射颜色就不起作用

6、<stoke />改组件加边框线,看一下设置这个属性后,变为什么样子:

<stroke android:width="1dip"
        android:color="#0000ff"
        android:dashWidth="5dip"
        android:dashGap="5dip"/>

分别表示:外边框颜色宽度为1,颜色为蓝色,破折号长度为5,破折号之间的间距为5.效果截图如下:

到这里shape的基本用法就介绍完了。学习完shape基本用法,在Android简易实战系列第十七话中,完成一个自定义彩色进度条的案例

作者:qq_32059827 发表于2016/8/14 11:02:12 原文链接
阅读:118 评论:0 查看评论

0202 - 应用组件 - Intent 和 Intent 过滤器

$
0
0

Intent 和 Intent 过滤器

来源:Android Develop - API Guides - App Components - Intents and Intent Filters


Intent 是一个消息传递对象,您可以使用它从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:

  • 启动 Activity

    Activity 表示应用中的一个屏幕。通过将 Intent 传递给 startActivity(),您可以启动新的 Activity 实例。Intent 描述了要启动的 Activity,并携带了任何必要的数据。

    如果您希望在 Activity 完成后收到结果,请调用 startActivityForResult()。在 Activity 的onActivityResult() 回调中,您的 Activity 将结果作为单独的 Intent 对象接收。如需了解详细信息,请参阅Activity指南。

  • 启动服务

    Service 是一个不使用用户界面而在后台执行操作的组件。通过将 Intent 传递给 startService(),您可以启动服务执行一次性操作(例如,下载文件)。Intent 描述了要启动的服务,并携带了任何必要的数据。

    如果服务旨在使用客户端-服务器接口,则通过将 Intent 传递给 bindService(),您可以从其他组件绑定到此服务。如需了解详细信息,请参阅服务指南。

  • 传递广播

    广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将 Intent 传递给 sendBroadcast()sendOrderedBroadcast() 或 sendStickyBroadcast(),您可以将广播传递给其他应用。

Intent 类型


Intent 分为两种类型:

  • 显式 Intent :按名称(完全限定类名)指定要启动的组件。通常,您会在自己的应用中使用显式 Intent 来启动组件,这是因为您知道要启动的 Activity 或服务的类名。例如,启动新 Activity 以响应用户操作,或者启动服务以在后台下载文件。
  • 隐式 Intent :不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它。例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。

创建显式 Intent 启动 Activity 或服务时,系统将立即启动 Intent 对象中指定的应用组件。

图 1. 隐式 Intent 如何通过系统传递以启动其他 Activity 的图解:[1] Activity A 创建包含操作描述的 Intent,并将其传递给 startActivity()[2] Android 系统搜索所有应用中与 Intent 匹配的 Intent 过滤器。找到匹配项之后,[3] 该系统通过调用匹配 Activity(Activity B)的 onCreate() 方法并将其传递给Intent,以此启动匹配 Activity。

创建隐式 Intent 时,Android 系统通过将 Intent 的内容与在设备上其他应用的清单文件中声明的 Intent 过滤器进行比较,从而找到要启动的相应组件。Intent如果 Intent 与 Intent 过滤器匹配,则系统将启动该组件,并将其传递给对象。如果多个 Intent 过滤器兼容,则系统会显示一个对话框,支持用户选取要使用的应用。

Intent 过滤器是应用清单文件中的一个表达式,它指定该组件要接收的 Intent 类型。例如,通过为 Activity 声明 Intent 过滤器,您可以使其他应用能够直接使用某一特定类型的 Intent 启动 Activity。同样,如果您没有为 Activity 声明任何 Intent 过滤器,则 Activity 只能通过显式 Intent 启动。

警告:为了确保应用的安全性,启动 Service 时,请始终使用显式 Intent,且不要为服务声明 Intent 过滤器。使用隐式 Intent 启动服务存在安全隐患,因为您无法确定哪些服务将响应 Intent,且用户无法看到哪些服务已启动。从 Android 5.0(API 级别 21)开始,如果使用隐式 Intent 调用 bindService(),系统会抛出异常。

构建 Intent


Intent 对象携带了 Android 系统用来确定要启动哪个组件的信息(例如,准确的组件名称或应当接收该 Intent 的组件类别),以及收件人组件为了正确执行操作而使用的信息(例如,要采取的操作以及要处理的数据)。

Intent 中包含的主要信息如下:

组件名称
要启动的组件名称。

这是可选项,但也是构建显式 Intent 的一项重要信息,这意味着 Intent 应当仅传递给由组件名称定义的应用组件。如果没有组件名称,则 Intent 是隐式的,且系统将根据其他 Intent 信息(例如,以下所述的操作、数据和类别)决定哪个组件应当接收 Intent。因此,如需在应用中启动特定的组件,则应指定该组件的名称。

注意:启动 Service 时,您应 始终指定组件名称。否则,您无法确定哪项服务会响应 Intent,且用户无法看到哪项服务已启动。

Intent 的这一字段是 ComponentName 对象,您可以使用目标组件的完全限定类名指定此对象,其中包括应用的软件包名称。例如,com.example.ExampleActivity。您可以使用 setComponent()setClass()setClassName() 或 Intent 构造函数设置组件名称。

操作
指定要执行的通用操作(例如,“查看”或“选取”)的字符串。

对于广播 Intent,这是指已发生且正在报告的操作。操作在很大程度上决定了其余 Intent 的构成,特别是数据和 extra 中包含的内容。

您可以指定自己的操作,供 Intent 在您的应用内使用(或者供其他应用在您的应用中调用组件)。但是,您通常应该使用由 Intent 类或其他框架类定义的操作常量。以下是一些用于启动 Activity 的常见操作:

ACTION_VIEW
如果您拥有一些某项 Activity 可向用户显示的信息(例如,要使用图库应用查看的照片;或者要使用地图应用查找的地址),请使用 Intent 将此操作与 startActivity() 结合使用。
ACTION_SEND
这也称为“共享” Intent。如果您拥有一些用户可通过其他应用(例如,电子邮件应用或社交共享应用)共享的数据,则应使用 Intent 中将此操作与 startActivity() 结合使用。

有关更多定义通用操作的常量,请参阅Intent类引用。 其他操作在 Android 框架中的其他位置定义。例如,对于在系统的设置应用中打开特定屏幕的操作,将在 Settings 中定义。

您可以使用 setAction() 或 Intent 构造函数为 Intent 指定操作。

如果定义自己的操作,请确保将应用的软件包名称作为前缀。 例如:

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
数据
引用待操作数据和/或该数据 MIME 类型的 URI(Uri 对象)。提供的数据类型通常由 Intent 的操作决定。例如,如果操作是 ACTION_EDIT,则数据应包含待编辑文档的 URI。

创建 Intent 时,除了指定 URI 以外,指定数据类型(其 MIME 类型)往往也很重要。例如,能够显示图像的Activity可能无法播放音频文件,即便 URI 格式十分类似时也是如此。因此,指定数据的 MIME 类型有助于 Android 系统找到接收 Intent 的最佳组件。但有时,MIME 类型可以从 URI 中推断得出,特别当数据是 content: URI 时尤其如此。这表明数据位于设备中,且由 ContentProvider 控制,这使得数据 MIME 类型对系统可见。

要仅设置数据 URI,请调用 setData()。要仅设置 MIME 类型,请调用 setType()。如有必要,您可以使用 setDataAndType() 同时显式设置二者。

警告:若要同时设置 URI 和 MIME 类型,请勿调用 setData() 和 setType(),因为它们会互相抵消彼此的值。请始终使用 setDataAndType()同时设置 URI 和 MIME 类型。

类别
一个包含应处理 Intent 组件类型的附加信息的字符串。您可以将任意数量的类别描述放入一个 Intent 中,但大多数 Intent 均不需要类别。以下是一些常见类别:
CATEGORY_BROWSABLE
目标 Activity 允许本身通过 Web 浏览器启动,以显示链接引用的数据,如图像或电子邮件。
CATEGORY_LAUNCHER
该 Activity 是任务的初始 Activity,在系统的应用启动器中列出。

有关类别的完整列表,请参阅 Intent 类描述。

您可以使用 addCategory() 指定类别。

以上列出的这些属性(组件名称、操作、数据和类别)表示 Intent 的既定特征。通过读取这些属性,Android 系统能够解析应当启动哪个应用组件。

但是,Intent 也有可能会一些携带不影响其如何解析为应用组件的信息。Intent 还可以提供:

Extra
携带完成请求操作所需的附加信息的键值对。正如某些操作使用特定类型的数据 URI 一样,有些操作也使用特定的附加数据。

您可以使用各种 putExtra() 方法添加附加数据,每种方法均接受两个参数:键名和值。您还可以创建一个包含所有附加数据的 Bundle 对象,然后使用 putExtras() 将 Bundle 插入 Intent 中。

例如,使用 ACTION_SEND 创建用于发送电子邮件的 Intent 时,可以使用 EXTRA_EMAIL 键指定“目标”收件人,并使用 EXTRA_SUBJECT 键指定“主题”。

Intent 类将为标准化的数据类型指定多个 EXTRA_* 常量。如需声明自己的附加数据 键(对于应用接收的 Intent ),请确保将应用的软件包名称作为前缀。例如:

static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
标志
在 Intent 类中定义的、充当 Intent 元数据的标志。标志可以指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个 任务 ),以及启动之后如何处理(例如,它是否属于最近的 Activity 列表)。

如需了解详细信息,请参阅 setFlags() 方法。

显式 Intent 示例

显式 Intent 是指用于启动某个特定应用组件(例如,应用中的某个特定 Activity 或服务)的 Intent。要创建显式 Intent,请为 Intent 对象定义组件名称。Intent 的所有其他属性均为可选属性。

例如,如果在应用中构建了一个名为 DownloadService、旨在从 Web 中下载文件的服务,则可使用以下代码启动该服务:

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);

Intent(Context, Class) 构造函数分别为应用和组件提供 Context 和 Class 对象。因此,此 Intent 将显式启动该应用中的 DownloadService 类。

如需了解有关构建和启动服务的详细信息,请参阅服务指南。

隐式 Intent 示例

隐式 Intent 指定能够在可以执行相应操作的设备上调用任何应用的操作。如果您的应用无法执行该操作而其他应用可以,且您希望用户选取要使用的应用,则使用隐式 Intent 非常有用。

例如,如果您希望用户与他人共享您的内容,请使用 ACTION_SEND 操作创建 Intent,并添加指定共享内容的 Extra。使用该 Intent 调用 startActivity()时,用户可以选取共享内容所使用的应用。

警告:用户可能没有任何应用处理您发送到 startActivity() 的隐式 Intent。如果出现这种情况,则调用将会失败,且应用会崩溃。要验证 Activity 是否会接收 Intent,请对 Intent 对象调用 resolveActivity()。如果结果为非空,则至少有一个应用能够处理该 Intent,且可以安全调用startActivity()。如果结果为空,则不应使用该 Intent。如有可能,您应禁用发出该 Intent 的功能。

// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");

// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(sendIntent);
}

注意:在这种情况下,系统并没有使用 URI,但已声明 Intent 的数据类型,用于指定 Extra 携带的内容。

调用 startActivity() 时,系统将检查已安装的所有应用,确定哪些应用能够处理这种 Intent(即:含 ACTION_SEND 操作并携带“文本/纯”数据的 Intent )。如果只有一个应用能够处理,则该应用将立即打开并提供给 Intent。如果多个 Activity 接受 Intent,则系统将显示一个对话框,使用户能够选取要使用的应用。

图 2. 选择器对话框。

强制使用应用选择器

如果有多个应用响应隐式 Intent,则用户可以选择要使用的应用,并将其设置为该操作的默认选项。 如果用户可能希望今后一直使用相同的应用执行某项操作(例如,打开网页时,用户往往倾向于仅使用一种 Web 浏览器),则这一点十分有用。

但是,如果多个应用可以响应 Intent,且用户可能希望每次使用不同的应用,则应采用显式方式显示选择器对话框。选择器对话框要求用户选择每次操作要使用的应用(用户无法为该操作选择默认应用)。 例如,当应用使用ACTION_SEND 操作执行“共享”时,用户根据目前的状况可能需要使用另一不同的应用,因此应当始终使用选择器对话框,如图 2 中所示。

要显示选择器,请使用 createChooser() 创建 Intent,并将其传递给 startActivity()。例如:

Intent sendIntent = new Intent(Intent.ACTION_SEND);
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

这将显示一个对话框,其中包含响应传递给 createChooser() 方法的 Intent 的应用列表,并使用提供的文本作为对话框标题。

接收隐式 Intent


要公布应用可以接收哪些隐式 Intent,请在清单文件中使用 &lt;intent-filter&gt; 元素为每个应用组件声明一个或多个 Intent 过滤器。每个 Intent 过滤器均根据 Intent 的操作、数据和类别指定自身接受的 Intent 类型。仅当隐式 Intent 可以通过 Intent 过滤器之一传递时,系统才会将该 Intent 传递给应用组件。

注意:显式 Intent 始终会传递给其目标,无论组件声明的 Intent 过滤器如何均是如此。

应用组件应当为自身可执行的每个独特作业声明单独的过滤器。例如,图像库应用中的一个 Activity 可能会有两个过滤器,分别用于查看图像和编辑图像。 当 Activity 启动时,它将检查 Intent 并根据 Intent 中的信息决定具体的行为(例如,是否显示编辑器控件)。

每个 Intent 过滤器均由应用清单文件中的 &lt;intent-filter&gt; 元素定义,并嵌套在相应的应用组件(例如,&lt;activity&gt; 元素)中。在&lt;intent-filter&gt; 内部,您可以使用以下三个元素中的一个或多个指定要接受的 Intent 类型:

&lt;action&gt;
在 name 属性中,声明接受的 Intent 操作。该值必须是操作的文本字符串值,而不是类常量。
&lt;data&gt;
使用一个或多个指定 数据 URI(schemehostportpath 等)各个方面和 MIME 类型的属性,声明接受的数据类型。
&lt;category&gt;
在 name 属性中,声明接受的 Intent 类别。该值必须是操作的文本字符串值,而不是类常量。

注意:为了接收隐式 Intent,必须将 CATEGORY_DEFAULT 类别包括在 Intent 过滤器中。方法 startActivity() 和 startActivityForResult()将按照已申明 CATEGORY_DEFAULT 类别的方式处理所有 Intent。 如果未在 Intent 过滤器中声明此类别,则隐式 Intent 不会解析为您的 Activity。

例如,以下是一个使用 Intent 过滤器进行的 Activity 声明,当数据类型为文本时,系统将接收 ACTION_SEND Intent :

<activity android:name="ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

您可以创建一个包括多个 &lt;action&gt;&lt;data&gt; 或 &lt;category&gt; 实例的过滤器。创建时,仅需确定组件能够处理这些过滤器元素的任何及所有组合即可。

如需仅以操作、数据和类别类型的特定组合来处理多种 Intent,则需创建多个 Intent 过滤器。

系统通过将 Intent 与所有这三个元素进行比较,根据过滤器测试隐式 Intent。隐式 Intent 若要传递给组件,必须通过所有这三项测试。如果 Intent 甚至无法匹配其中任何一项测试,则 Android 系统不会将其传递给组件。但是,由于一个组件可能有多个 Intent 过滤器,因此未能通过某一组件过滤器的 Intent 可能会通过另一过滤器。如需了解有关系统如何解析 Intent 的详细信息,请参阅下文的 Intent 解析部分。

警告:为了避免无意中运行不同应用的 Service,请始终使用显式 Intent 启动您自己的服务,且不必为该服务声明 Intent 过滤器。

注意: 对于所有 Activity,您必须在清单文件中声明 Intent 过滤器。但是,广播接收器的过滤器可以通过调用 registerReceiver() 动态注册。稍后,您可以使用 unregisterReceiver() 注销该接收器。这样一来,应用便可仅在应用运行时的某一指定时间段内侦听特定的广播。

过滤器示例

为了更好地了解一些 Intent 过滤器的行为,我们一起来看看从社交共享应用的清单文件中截取的以下片段。

<activity android:name="MainActivity">
    <!-- This activity is the main entry, should appear in app launcher -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name="ShareActivity">
    <!-- This activity handles "SEND" actions with text data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <action android:name="android.intent.action.SEND_MULTIPLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="video/*"/>
    </intent-filter>
</activity>

第一个 Activity MainActivity 是应用的主要入口点。当用户最初使用启动器图标启动应用时,该 Activity 将打开:

这两个元素必须配对使用,Activity 才会显示在应用启动器中。

第二个 Activity ShareActivity 旨在便于共享文本和媒体内容。尽管用户可以通过从 MainActivity 导航进入此 Activity,但也可以从发出隐式 Intent(与两个 Intent 过滤器之一匹配)的另一应用中直接进入 ShareActivity

注意:MIME 类型 application/vnd.google.panorama360+jpg 是一个指定全景照片的特殊 数据类型,您可以使用 Google 全景 API 对其进行处理。

使用待定 Intent


PendingIntent 对象是 Intent 对象的包装器。PendingIntent 的主要目的是授权外部应用使用包含的 Intent,就像是它从您应用本身的进程中执行的一样。

待定 Intent 的主要用例包括:

由于每个 Intent 对象均设计为由特定类型的应用组件进行处理(ActivityService 或 BroadcastReceiver),因此还必须基于相同的考虑因素创建PendingIntent。使用待定 Intent 时,应用不会使用调用(如 startActivity())执行该 Intent。相反,通过调用相应的创建器方法创建 PendingIntent时,您必须声明所需的组件类型:

除非您的应用正在从其他应用中接收待定 Intent,否则上述用于创建 PendingIntent 的方法可能是您所需的唯一 PendingIntent 方法。

每种方法均会提取当前的应用 Context、您要包装的 Intent 以及一个或多个指定应如何使用该 Intent 的标志(例如,是否可以多次使用该 Intent)。

如需了解有关使用待定 Intent 的详细信息,请参阅通知 和应用小工具 API 指南等手册中每个相应用例的相关文档。

Intent 解析


当系统收到隐式 Intent 以启动 Activity 时,它根据以下三个方面将该 Intent 与 Intent 过滤器进行比较,搜索该 Intent 的最佳 Activity:

  • Intent 操作
  • Intent 数据(URI 和数据类型)
  • Intent 类别

下文根据如何在应用的清单文件中声明 Intent 过滤器,描述 Intent 如何与相应的组件匹配。

操作测试

要指定接受的 Intent 操作, Intent 过滤器既可以不声明任何 &lt;action&gt; 元素,也可以声明多个此类元素。例如:

<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="android.intent.action.VIEW" />
    ...
</intent-filter>

要通过此过滤器,您在 Intent 中指定的操作必须与过滤器中列出的某一操作匹配。

如果该过滤器未列出任何操作,则 Intent 没有任何匹配项,因此所有 Intent 均无法通过测试。但是,如果 Intent 未指定操作,则会通过测试(只要过滤器至少包含一个操作)。

类别测试

要指定接受的 Intent 类别, Intent 过滤器既可以不声明任何 &lt;category&gt; 元素,也可以声明多个此类元素。例如:

<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    ...
</intent-filter>

若要 Intent 通过类别测试,则 Intent 中的每个类别均必须与过滤器中的类别匹配。反之则未必然,Intent 过滤器声明的类别可以超出 Intent 中指定的数量,且 Intent 仍会通过测试。因此,不含类别的 Intent 应当始终会通过此测试,无论过滤器中声明何种类别均是如此。

注意: Android 会自动将 CATEGORY_DEFAULT 类别应用于传递给 startActivity() 和 startActivityForResult() 的所有隐式 Intent。因此,如需 Activity 接收隐式 Intent,则必须将 "android.intent.category.DEFAULT" 的类别包括在其 Intent 过滤器中(如上文的 &lt;intent-filter&gt; 示例所示)。

数据测试

要指定接受的 Intent 数据, Intent 过滤器既可以不声明任何 &lt;data&gt; 元素,也可以声明多个此类元素。例如:

<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    ...
</intent-filter>

每个 <data> 元素均可指定 URI 结构和数据类型(MIME 介质类型)。URI 的每个部分均包含单独的 schemehostport 和 path 属性:

&lt;scheme&gt;://&lt;host&gt;:&lt;port&gt;/&lt;path&gt;

例如:

content://com.example.project:200/folder/subfolder/etc

在此 URI 中,架构是 content,主机是 com.example.project,端口是 200,路径是 folder/subfolder/etc

在 &lt;data&gt; 元素中,上述每个属性均为可选,但存在线性依赖关系:

  • 如果未指定架构,则会忽略主机。
  • 如果未指定主机,则会忽略端口。
  • 如果未指定架构和主机,则会忽略路径。

将 Intent 中的 URI 与过滤器中的 URI 规范进行比较时,它仅与过滤器中包含的部分 URI 进行比较。例如:

  • 如果过滤器仅指定架构,则具有该架构的所有 URI 均与该过滤器匹配。
  • 如果过滤器指定架构和权限、但未指定路径,则具有相同架构和权限的所有 URI 都会通过过滤器,无论其路径如何均是如此。
  • 如果过滤器指定架构、权限和路径,则仅具有相同架构、权限和路径 的 URI 才会通过过滤器。

注意:路径规范可以包含星号通配符 (*),因此仅需部分匹配路径名即可。

数据测试会将 Intent 中的 URI 和 MIME 类型与过滤器中指定的 URI 和 MIME 类型进行比较。规则如下:

  1. 仅当过滤器未指定任何 URI 或 MIME 类型时,不含 URI 和 MIME 类型的 Intent 才会通过测试。
  2. 对于包含 URI、但不含 MIME 类型(既未显式声明,也无法通过 URI 推断得出)的 Intent,仅当其 URI 与过滤器的 URI 格式匹配、且过滤器同样未指定 MIME 类型时,才会通过测试。
  3. 仅当过滤器列出相同的 MIME 类型且未指定 URI 格式时,包含 MIME 类型、但不含 URI 的 Intent 才会通过测试。
  4. 仅当 MIME 类型与过滤器中列出的类型匹配时,包含 URI 和 MIME 类型(通过显式声明,或可以通过 URI 推断得出)的 Intent 才会通过测试的 MIME 类型部分。如果 Intent 的 URI 与过滤器中的 URI 匹配,或者如果 Intent 具有 content: 或 file: URI 且过滤器未指定 URI,则 Intent 会通过测试的 URI 部分。换而言之,如果过滤器仅列出 MIME 类型,则假定组件支持 content: 和 file: 数据。

最后一条规则,即规则 (d),反映了期望组件能够从文件中或内容提供商处获得本地数据。因此,其过滤器可以仅列出数据类型,而不必显式命名content: 和 file: 架构。这是一个典型的案例。例如,下文中的 &lt;data&gt; 元素向 Android 指出,组件可从内容提供商处获得并显示图像数据:

<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>

由于大部分可用数据均由内容提供商分发,因此指定数据类型(而非 URI)的过滤器也许最为常见。

另一常见的配置是具有架构和数据类型的过滤器。例如,下文中的 &lt;data&gt; 元素向 Android 指出,组件可从网络中检索视频数据以执行操作:

<intent-filter>
    <data android:scheme="http" android:type="video/*" />
    ...
</intent-filter>

Intent 匹配

通过 Intent 过滤器匹配 Intent,这不仅有助于发现要激活的目标组件,还有助于发现设备上组件集的相关信息。例如,主页应用通过使用指定ACTION_MAIN 操作和 CATEGORY_LAUNCHER 类别的 Intent 过滤器查找所有 Activity,以此填充应用启动器。

您的应用可以采用类似的方式使用 Intent 匹配。PackageManager 提供了一整套 query...() 方法来返回所有能够接受特定 Intent 的组件。此外,它还提供了一系列类似的 resolve...() 方法来确定响应 Intent 的最佳组件。例如,queryIntentActivities() 将返回能够执行那些作为参数传递的 Intent 的所有 Activity 列表,而 queryIntentServices() 则可返回类似的服务列表。这两种方法均不会激活组件,而只是列出能够响应的组件。对于广播接收器,有一种类似的方法: queryBroadcastReceivers()


作者:qzw9231000 发表于2016/8/14 11:19:41 原文链接
阅读:78 评论:0 查看评论

1201 - 数据存储 - 如何选择数据存储

$
0
0

如何选择数据存储

来源:Android Develop - API Guides - Data Storage - Storage Options

Android给提供了几种应用数据持久化的选择,如何选择数据存储的解决方案要根据我们的特殊需要,比如根据数据是应该对应用私有还是数据可以让其他应用(或用户)获取,以及数据需要多大的空间来存储。

数据存储的解决方案有如下几种:

Shared Preferences
以键值对的形式存储私有的、简单的数据。
Internal Storage
在设备内部存储中存储私有数据。
External Storage
在共享的外部存储中存储公共数据。
SQLite Databases
在私有的数据库中存储结构化的数据。
Network Connection
用我们自己的网络服务器在网络中存储数据。

Android提供了一种将数据暴露给其他应用的方式 — 使用content provider。 Content provider 是可选的组件,它可以暴露应用数据的 读/写 使用权限, 并且可以在数据的使用上添加我们需要的任何限制条件。 您可以在Content Providers开发者指南中了解有关它的更多详情。

Shared Preference 应用


SharedPreferences 类提供了一套框架,我们可以用其存储以及恢复的私有的、持久化的、键值对类型的数据。我们可以使用 SharedPreferences 来存储任何类型的私有数据: boolean、float、int、long、string。数据会在用户活动时会持久存在 (即便应用被kill掉)。

以下是为应用获得 SharedPreferences 的两种方法:

  • getSharedPreferences() - 如果需要用到多个preferences使用这个方法,第一个参数为用来标识不同preferences的名字。(这里的preferences属于应用程序)
  • getPreferences() - 如果你的Activity只需要使用一个preference则调用这个方法来获取,因为这个preferences是你的Activity的唯一一个,不需要提供名字来获取。

写入数据:

  1. 调用 SharedPreferences的 edit() 方法来获取 SharedPreferences.Editor
  2. 使用 putBoolean() 或者 putString()等等类似的方法来添加数据。
  3. 使用 commit()方法来提交新添加的数据。

读取数据:使用SharedPreferences 的 getBoolean() 以及getString()等等类似的方法来获取数据。

以下为在计算器应用中使用preferences存取静音按键模式设置的例子:

public class Calc extends Activity {
    public static final String PREFS_NAME = "MyPrefsFile";

    @Override
    protected void onCreate(Bundle state){
       super.onCreate(state);
       . . .

       // Restore preferences
       SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
       boolean silent = settings.getBoolean("silentMode", false);
       setSilent(silent);
    }

    @Override
    protected void onStop(){
       super.onStop();

      // We need an Editor object to make preference changes.
      // All objects are from android.context.Context
      SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
      SharedPreferences.Editor editor = settings.edit();
      editor.putBoolean("silentMode", mSilentMode);

      // Commit the edits!
      editor.commit();
    }
}

Internal Storage 使用


我们可以将文件直接存到 internal storage 中。 缺省情况下,存到internal storage中的文件对我们的应用是私有的,其他应用(以及用户)都没有办法获取它们。当用户卸载我们的应用时,这些文件也会被移除。 

在 internal storage 中创建文件并写入数据:

  1. 调用 openFileOutput() 方法并传入文件名以及操作模式两个参数以获取到 FileOutputStream
  2. 调用 write()方法来将数据写入文件。
  3. 使用 close()方法来关闭stream。

例:

String FILENAME = "hello_file";
String string = "hello world!";

FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();

MODE_PRIVATE 会创建一个文件 (或者替换一个同名文件) 并使其对应用私有。 其他操作模式有: MODE_APPEND,MODE_WORLD_READABLE, and MODE_WORLD_WRITEABLE.

注意:  MODE_WORLD_READABLE 和MODE_WORLD_WRITEABLE 在 API level 17之后已经过时。 Android N 版本开始,使用它们的话会抛出 SecurityException 异常. 这意味着应用的目标版本如果为 Android N 或者更高的话,则不可以通过文件名来共享私有文件,并且尝试共享像这样的"file://" URI 也会导致 FileUriExposedException 异常被抛出。如果你的应用需要和其他应用共享私有文件,需要使用 FileProvider 并传入 FLAG_GRANT_READ_URI_PERMISSION。具体请看 Android Develop - Training - Building Apps With Contents Sharing - Sharing Files

从 internal storage中读文件:

  1. 调用openFileInput()方法并传入文件名以返回FileInputStream
  2. 使用read()方法来读取bytes。
  3. 使用close()方法来关闭stream。

注意: 如果你希望在应用中存储一个编译期的静态文件(即应用编译打包时不会对文件作任何处理),那么请将这个文件存到 res/raw/ 目录下。 你可以使用openRawResource()方法来打开它,同时需要传入类似 R.raw.<filename> 的资源 ID。这个方法会返回 InputStream ,你可以用它来读取文件 (但是不可以对原始的文件进行写操作)。

存储缓存文件

如果你想缓存一些数据而不想将其持久化存储,你应该调用getCacheDir() 来打开一个 File ,它是位于 internal storage 中的应用存储自身临时缓存文件的目录。

当设备的 internal storage 存储空间所剩不多时,Android 会删掉这些缓存来恢复存储空间。然而,你不应该依靠系统来为你清理这些文件。 你应该自行维护这些缓存文件,让其保持合理的空间消耗,比如1MB。当用户卸载了你的应用时,这些缓存文件也会被移除。

其他比较有用的方法

getFilesDir()
得到你的内部文件存储目录的绝对路径。
getDir()
在internal storage 空间中创建一个(或打开已经存在的) 目录。
deleteFile()
删除一个存储在internal storage 空间中的文件。
fileList()
返回现在存储在应用中的文件列表。

External Storage 使用


每个兼容Android系统的设备都支持一个共享的 external storage ,我们可以用它来存储文件。 它可以是可移动的存储媒介 (例如一个SD卡) ,或者位于 手机内部存储中(不可移动)。存储在 external storage 中的文件对所有应用都是可读的,并且当用户使用USB以文件传输模式将设备连接到电脑时。用户也可以去更改 external storage 中的文件。

提醒: 当用户将 external storage 挂载到电脑上或者移除了这些存储媒介时,external storage 会变得不可用,并且存储在 external storage 中的文件没有强制的安全措施来维护,所有应用都可以读写这些存储在 external storage 中的文件并且用户也可以删除它们。

获取 external storage

如果需要对 external storage 进行文件的读写操作,应用需要获取 READ_EXTERNAL_STORAGE 或者 WRITE_EXTERNAL_STORAGE 系统权限。例:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

如果你既要对文件进行读操作,又要进行写操作, 那你只要获取 WRITE_EXTERNAL_STORAGE 这一个权限,因为它也包含了对文件的读操作的权限。

注意: 自 Android 4.4开始, 如果你只是读写你的应用的私有文件,将不再需要获取这些权限,更多内容请看下面的 saving files that are app-private (存储应用私有的文件)这个章节。

检查存储媒介是否可用

在你对 external storage 进行任何操作之前,你应该先调用 getExternalStorageState() 这个方法来检查当前存储媒介是否可用。External storage 的存储可能被挂载到了电脑上、缺失、只读或者在其它状态下。下面是两个用于检查存储可用性的方法的例子

/* 检查 external storage 是否可读写 */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

/* 检查 external storage 是否为只读 */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

这个 getExternalStorageState() 方法也会返回其它你希望检查的状态,例如存储是否正在被共享(即挂载到了电脑上)、是否完全缺失、是否已经被移除,当你的应用需要获取而无法获取 external storage 时你可以去检查状态以用更多的信息来通知用户。

存储应用共享文件

通常用户通过应用获取的文件应该在设备上的公共的位置进行存储,其他的应用可以获取这些存储在公共的位置中的文件并且用户也可以轻易从设备上复制它们,当这样做的时候,你应该使用一个公共的文件目录,例如 Music/、Pictures/、Ringtones/。

调用 getExternalStoragePublicDirectory()来获取公共的文件目录 File ,方法中需要传入你想获取的文件目录类型例如 DIRECTORY_MUSICDIRECTORY_PICTURES,DIRECTORY_RINGTONES 或者其他的。通过将你的文件存储到与文件类型相一致的文件目录中,系统的 media 扫描可以合理的对你的文件进行分类 (例如,铃声文件在系统设置中会以铃声类型出现,而不是音乐类型)。

下面是一个在公共的图片目录中创建一个新的相册的方法的例子:

public File getAlbumStorageDir(String albumName) {
    // 获取用户放置公共图片的文件目录
    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

存储应用私有的文件

如果你需要处理一些不希望被其他应用使用的文件 (例如仅仅在你的应用中使用的图形纹理或声音效果),你应该调用 getExternalFilesDir()这个方法以使用在 external storage 中的私有文件目录,这个方法也需要一个 type 参数来指明子文件目录的类型 (例如 DIRECTORY_MOVIES)。如果你不需要一个特定的 media 目录,那么可以传入 null 来获取应用的私有文件目录的根目录。

自 Android 4.4 开始,在应用私有文件目录中读写文件不需要再获取 READ_EXTERNAL_STORAGE 或者 WRITE_EXTERNAL_STORAGE 权限。所以你可以通过加入maxSdkVersion 属性来为需要获取这两个权限的低版本来申请权限。

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                     android:maxSdkVersion="18" />
    ...
</manifest>

注意: 当用户删除了你的应用,这个私有文件目录里的所有的文件内容都会被删除,并且系统的 media 扫描不会来读取这个目录中的文件,所以这些文件不可以在 MediaStore content provider中获取到。同样地, 你 不应该使用这个私有文件目录 存储属于用户的 media 文件,例如应用拍摄或修改的照片以及用户购买的音乐等,这些应该被存储在公共的文件目录中,详细请看上面的 saved in the public directories (存储应用共享文件)章节。

有时候,设备会从手机内部存储中分配一个分区给 external storage 使用,或者也会给其提供一个SD卡卡槽。当设备在 Android 4.3 或更低的版本中运行时, 这个 getExternalFilesDir() 方法仅仅能够获取在手机内部存储中的 external storage ,应用没有办法通过这个方法来对SD卡进行读写操作。 然而自Android 4.4 开始,调用 getExternalFilesDirs()方法可以获取手机内部存储以及SD卡的 external storage 两个位置,其会返回一个 File数组来分别表示不同的存储位置。数组中的第一个值为首要的 external storage ,并且你不应该去使用这个位置,除非它满了或者不可用了。如果你希望在Android 4.3 或更低版本中获取也能够获取不同的存储位置,请使用 support library's 中的静态方法 ContextCompat.getExternalFilesDirs()。这个也可以返回一个 File 数组。但是在 Android 4.3 或者更低版本中,数组中通常只包含一个值。

提醒:尽管 getExternalFilesDir() 和getExternalFilesDirs() 返回的文件目录不可以被 MediaStore content provider获取,但其他应用如果获取了 READ_EXTERNAL_STORAGE 权限的话则可以取到所有 external storage 中的文件,包含这些 external storage 中私有文件目录中的文件。如果你需要完全限制其他应用对你的文件的获取,那你应该将文件存储在 internal storage 中而不是  external storage 中。

存储缓存文件

如果要打开在  external storage 中存储缓存文件的文件目录,请调用 getExternalCacheDir()方法 ,如果用户卸载了这个应用,这些缓存文件也会被删除。

类似上面提及的 ContextCompat.getExternalFilesDirs()方法,你也可以调用ContextCompat.getExternalCacheDirs()方法来获取多个 external storage (如果存在第二个 external storage )中的缓存文件目录。

注意: 你需要谨慎的管理缓存文件并且通过应用的生命周期来移除那些不再需要用到的缓存文件,这对于维护文件空间以及应用性能而言非常重要。

Databases 使用


Android提供了对 SQLite 数据库的完整的支持。你创建的任何数据库都可以在应用中的任何类中通过数据库名称来获取到,但是其他应用不能获取。

创建一个新的 SQLite 数据库的推荐方法:创建 SQLiteOpenHelper 的子类并覆盖其 onCreate() 方法,在 onCreate() 方法中你可以执行 SQLite 命令以在数据库中创建表格,例:

public class DictionaryOpenHelper extends SQLiteOpenHelper {

    private static final int DATABASE_VERSION = 2;
    private static final String DICTIONARY_TABLE_NAME = "dictionary";
    private static final String DICTIONARY_TABLE_CREATE =
                "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" +
                KEY_WORD + " TEXT, " +
                KEY_DEFINITION + " TEXT);";

    DictionaryOpenHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(DICTIONARY_TABLE_CREATE);
    }
}

之后你可以获取你自定义了构造器的 SQLiteOpenHelper 的实例,如果需要读写数据库,则需要分别调用getWritableDatabase() 和getReadableDatabase()方法,它们都会返回一个 SQLiteDatabase ,其代表数据库并提供了对 SQLite 数据库操作的方法

你可以使用 SQLiteDatabase query() 方法来执行 SQLite 的查询,这个方法可以接受多个查询的参数,例如需要查询的表格、查询的行及其条件、查询的列、如何分组等等。更复杂的查询,比如需要列名称的别名等,你应该使用 SQLiteQueryBuilder,它提供了几个方便的方法来构建查询参数。

SQLite 查询会返回一个 Cursor,它指向了查询返回的所有行。 你可以使用这个 Cursor 来对数据库查询的结果进行操作,并且可以从中读取查询记录的行和列。

这是两个Android 使用 SQLite 数据库的demo, Note Pad ,Searchable Dictionary 。

数据库调试

Android SDK 包含一套 sqlite3 数据库工具,其可以让我们浏览数据库表格内容、执行SQL命令以及完成其他与 SQLite 数据库相关的功能。具体如何使用请查看 Examining sqlite3 databases from a remote shell 。(PS:就是adb里面那些对数据库操作的指令)

Network Connection 使用


你可以使用网络 (当其可用时)在网络服务器上存储和恢复数据,网络的操作需要用到下面两个包中的类:



作者:qzw9231000 发表于2016/8/14 11:25:52 原文链接
阅读:17 评论:0 查看评论

《React-Native系列》14、 RN学习之NodeJS

$
0
0

由于ReactNative使用了NodeJS环境,以前没有系统学习过NodeJS,故花点时间学习下。了解ReactNative里关于NodeJS部分的知识。

我们先看看一个RN项目的目录,如下:


我们看到的node_modules 和package.json都是和NodeJS相关联的。 学习完这边文章你就能明白:

1、我们在调试时,使用的包服务器,启动命令:npm start ,是什么意思?

2、我们在down一个项目下来以后,为什么要执行下:npm install ?

3、我们如果管理我们的node资源包?包括下载、更新、发布。


模块

编写稍大一点的程序时一般都会将代码模块化。在NodeJS中,一般将代码合理拆分到不同的JS文件中,每一个文件就是一个模块,而文件路径就是模块名。

在编写每个模块时,都有requireexportsmodule三个预先定义好的变量可供使用。

require

require函数用于在当前模块中加载和使用别的模块,传入一个模块名,返回一个模块导出对象。模块名可使用相对路径(以./开头),或者是绝对路径(以/C:之类的盘符开头)。另外,模块名中的.js扩展名可以省略。以下是一个例子。

var foo1 = require('./foo');
var foo2 = require('./foo.js');
var foo3 = require('/home/user/foo');
var foo4 = require('/home/user/foo.js');

// foo1至foo4中保存的是同一个模块的导出对象。

另外,可以使用以下方式加载和使用一个JSON文件,模块名中.json扩展名不可省略。

var data = require('./data.json');

exports

exports对象是当前模块的导出对象,用于导出模块公有方法和属性。别的模块通过require函数使用当前模块时得到的就是当前模块的exports对象。以下例子中导出了一个公有方法。

exports.hello = function () {
    console.log('Hello World!');
};

module

通过module对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块的导出对象。例如模块导出对象默认是一个普通对象,如果想改成一个函数的话,可以使用以下方式。

module.exports = function () {
    console.log('Hello World!');
};

以上代码中,模块默认导出对象被替换为一个函数。

模块初始化

一个模块中的JS代码仅在模块第一次被使用时执行一次,并在执行过程中初始化模块的导出对象。之后,缓存起来的导出对象被重复利用。

主模块

通过命令行参数传递给NodeJS以启动程序的模块被称为主模块。主模块负责调度组成整个程序的其它模块完成工作。例如通过以下命令启动程序时,main.js就是主模块。

$ node main.js

完整示例

例如有以下目录。

- /home/user/hello/
    - util/
        counter.js
    main.js

其中counter.js内容如下:

var i = 0;

function count() {
    return ++i;
}

exports.count = count;

该模块内部定义了一个私有变量i,并在exports对象导出了一个公有方法count

主模块main.js内容如下:

var counter1 = require('./util/counter');
var    counter2 = require('./util/counter');

console.log(counter1.count());
console.log(counter2.count());
console.log(counter2.count());

运行该程序的结果如下:

$ node main.js
1
2
3

可以看到,counter.js并没有因为被require了两次而初始化两次。

package.json

如果想自定义入口模块的文件名和存放位置,就需要在包目录下包含一个package.json文件,并在其中指定入口模块的路径。上例中的cat模块可以重构如下。

- /home/user/lib/
    - cat/
        + doc/
        - lib/
            head.js
            body.js
            main.js
        + tests/
        package.json

其中package.json内容如下。

{
    "name": "cat",
    "main": "./lib/main.js"
}

如此一来,就同样可以使用require('/home/user/lib/cat')的方式加载模块。NodeJS会根据包目录下的package.json找到入口模块所在位置。

NPM

NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:

  • 允许用户从NPM服务器下载别人编写的三方包到本地使用。

  • 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。

  • 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

可以看到,NPM建立了一个NodeJS生态圈,NodeJS开发者和用户可以在里边互通有无。以下分别介绍这三种场景下怎样使用NPM。

下载三方包

需要使用三方包时,首先得知道有哪些包可用。虽然npmjs.org提供了个搜索框可以根据包名来搜索,但如果连想使用的三方包的名字都不确定的话,就请百度一下吧。知道了包名后,比如上边例子中的argv,就可以在工程目录下打开终端,使用以下命令来下载三方包。

$ npm install argv
...
argv@0.0.2 node_modules\argv

下载好之后,argv包就放在了工程目录下的node_modules目录中,因此在代码中只需要通过require('argv')的方式就好,无需指定三方包路径。

以上命令默认下载最新版三方包,如果想要下载指定版本的话,可以在包名后边加上@<version>,例如通过以下命令可下载0.0.1版的argv

$ npm install argv@0.0.1
...
argv@0.0.1 node_modules\argv

如果使用到的三方包比较多,在终端下一个包一条命令地安装未免太人肉了。因此NPM对package.json的字段做了扩展,允许在其中申明三方包依赖。因此,上边例子中的package.json可以改写如下:

{
    "name": "node-echo",
    "main": "./lib/echo.js",
    "dependencies": {
        "argv": "0.0.2"
    }
}

这样处理后,在工程目录下就可以使用npm install命令批量安装三方包了。更重要的是,当以后node-echo也上传到了NPM服务器,别人下载这个包时,NPM会根据包中申明的三方包依赖自动下载进一步依赖的三方包。例如,使用npm install node-echo命令时,NPM会自动创建以下目录结构。

- project/
    - node_modules/
        - node-echo/
            - node_modules/
                + argv/
            ...
    ...

如此一来,用户只需关心自己直接使用的三方包,不需要自己去解决所有包的依赖关系。

安装命令行程序

从NPM服务上下载安装一个命令行程序的方法与三方包类似。例如上例中的node-echo提供了命令行使用方式,只要node-echo自己配置好了相关的package.json字段,对于用户而言,只需要使用以下命令安装程序。

$ npm install node-echo -g

参数中的-g表示全局安装,因此node-echo会默认安装到以下位置,并且NPM会自动创建好*nix系统下需要的软链文件或Windows系统下需要的.cmd文件。

- /usr/local/               # *nix系统下
    - lib/node_modules/
        + node-echo/
        ...
    - bin/
        node-echo
        ...
    ...

- %APPDATA%\npm\            # Windows系统下
    - node_modules\
        + node-echo\
        ...
    node-echo.cmd
    ...

发布代码

第一次使用NPM发布代码前需要注册一个账号。终端下运行npm adduser,之后按照提示做即可。账号搞定后,接着我们需要编辑package.json文件,加入NPM必需的字段。接着上边node-echo的例子,package.json里必要的字段如下。

{
    "name": "node-echo",           # 包名,在NPM服务器上须要保持唯一
    "version": "1.0.0",            # 当前版本号
    "dependencies": {              # 三方包依赖,需要指定包名和版本号
        "argv": "0.0.2"
      },
    "main": "./lib/echo.js",       # 入口模块位置
    "bin" : {
        "node-echo": "./bin/node-echo"      # 命令行程序名和主模块位置
    }
}

之后,我们就可以在package.json所在目录下运行npm publish发布代码了。

版本号

使用NPM下载和发布代码时都会接触到版本号。NPM使用语义版本号来管理代码,这里简单介绍一下。

语义版本号分为X.Y.Z三位,分别代表主版本号、次版本号和补丁版本号。当代码变更时,版本号按以下原则更新。

+ 如果只是修复bug,需要更新Z位。

+ 如果是新增了功能,但是向下兼容,需要更新Y位。

+ 如果有大变动,向下不兼容,需要更新X位。

版本号有了这个保证后,在申明三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某个范围的版本号。例如"argv": "0.0.x"表示依赖于0.0.x系列的最新版argv。NPM支持的所有版本号范围指定方式可以查看官方文档

灵机一点

除了本章介绍的部分外,NPM还提供了很多功能,package.json里也有很多其它有用的字段。除了可以在npmjs.org/doc/查看官方文档外,这里再介绍一些NPM常用命令。

  • NPM提供了很多命令,例如installpublish,使用npm help可查看所有命令。

  • 使用npm help <command>可查看某条命令的详细帮助,例如npm help install

  • package.json所在目录下使用npm install . -g可先在本地安装当前命令行程序,可用于发布前的本地测试。

  • 使用npm update <package>可以把当前目录下node_modules子目录里边的对应模块更新至最新版本。

  • 使用npm update <package> -g可以把全局安装的对应命令行程序更新至最新版。

  • 使用npm cache clear可以清空NPM本地缓存,用于对付使用相同版本号发布新版本代码的人。

  • 使用npm unpublish <package>@<version>可以撤销发布自己发布过的某个版本代码。


掌握上面NodeJS的知识点,对RN开发来说就足够用了,希望对你有用。

如果还想进一步学习NodeJS,可以移步:

阿里巴巴国际站前端技术部的blog:七天学会NodeJS


参考:http://www.lvtao.net/content/book/node.js.htm#8


作者:hsbirenjie 发表于2016/8/14 12:44:00 原文链接
阅读:81 评论:0 查看评论

Android简易实战教程--第十七话《自定义彩色环形进度条》

$
0
0

转载请注明出处:http://blog.csdn.net/qq_32059827/article/details/52203533   点击打开链接

在Android初级教程里面,介绍了shape用法的理论知识,再来完成这个小案例将会变得非常简单哦。(欢迎学习阅读):http://blog.csdn.net/qq_32059827/article/details/52203347 点击打开链接

这一篇就针对这个知识点,完成一个自定义的彩色进度条。系统自带的环形进度条是黑白相间的,如果你不是色盲,肯定觉得那个进度条其丑无比!就有必要设计一下它的状态,让我们给她点”颜色”看看。

首先看一下要设计的进度条长什么样子,动态截图如下:

接下来就一步步的完成这个效果。

还是老样子,在drawable目录下建一个shape类型的文件。里面代码如下:

在style里面加入如下代码:

<style name="ProgressBar">
        <item name="android:indeterminateOnly">true</item>
        <item name="android:indeterminateDrawable">@drawable/progressstyleshape</item>
        <item name="android:indeterminateBehavior">repeat</item>
        <item name="android:indeterminateDuration">3500</item>
        <item name="android:minWidth">60dip</item>
        <item name="android:maxWidth">60dip</item>
        <item name="android:minHeight">60dip</item>
        <item name="android:maxHeight">60dip</item>
        <item name="android:mirrorForRtl">false</item>
    </style>
在style里面引入了shape,drawable/progressstyleshape.xml文件里面的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="360" >

    <!--
    封装一个动画
    0~360°旋转、锚点位于中心
    -->
    <shape
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:innerRadiusRatio="3"
        android:shape="ring"
        android:thicknessRatio="10"
        android:useLevel="false" >
        <gradient
            android:endColor="#0000ff"
            android:startColor="#ff0000"
            android:type="sweep" />
        <!--
    android:shape="ring"系统默认矩形,这里修改为环形 
    android:innerRadiusRatio=""表示内半径比:半径/比值,这个值越大,环形的外环越细
	 android:thicknessRatio="10"表示厚度比
	android:useLevel="false"表示让进度条环形不切分,完全显示
	 gradint表示颜色的渐变
	android:type="sweep" 表示扫射的效果

        -->

    </shape>

</rotate>

然后再布局文件里面引入样式:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <ProgressBar
        android:layout_centerInParent="true"
        style="@style/ProgressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>

运行程序截图:



作者:qq_32059827 发表于2016/8/14 13:02:59 原文链接
阅读:101 评论:0 查看评论

NSArray/NSMutableArray创建,获取,遍历,排序 - iOS

$
0
0

1 数组基本

1.1 创建数组

NSArray 是不可变数组,一旦创建完成就不能够对数组进行,添加,删除等操作

下面这种方式相当于什么都没做

NSArray * array = [[NSArray alloc] init];

NSLog(@"%@",array);

1.2 通过构造方法的方式创建一个NSArray

在创建一个NSArray的时候,集合的最后一个元素一定是nil

NSArray * array1 = [NSArray arrayWithObjects:@"one",@"two",@"three", nil];
NSLog(@"%@",array1);

1.3 数组中可以存储不同类型的对象

NSNumber * number = [NSNumber numberWithInt:10];
NSArray * array2 = [[NSArray alloc] initWithObjects:@"one",@"two",number, nil];
NSLog(@"array2  %@",array2);

1.4 数组实际上存储的是对象的地址,同样也可以存储数组的地址

NSArray * a1 = [[NSArray alloc] initWithObjects:@"one",@"two",@"three", nil];
NSArray * a2 = [[NSArray alloc] initWithObjects:@"1",@"2",@"3", nil];
NSArray * a3 = [[NSArray alloc] initWithObjects:a1,a2, nil];

NSLog(@"a3 %@",a3);

1.5 存储自定义的对象

最好都 description 方法

@interface Person : NSObject

@property (nonatomic,strong)NSString * name;
@property (nonatomic,assign)int age;

- (id)initWithName:(NSString *)name andAge:(int)age;

@end
@implementation Person

- (id)initWithName:(NSString *)name andAge:(int)age
{
    if (self = [super init])
    {
        _name = name;
        _age = age;
    }
    return self;
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"age = %d name = %@",_age,_name];
}

@end
Person * p1 = [[Person alloc] initWithName:@"xiaozhe" andAge:20];
Person * p2 = [[Person alloc] initWithName:@"hell" andAge:18];
Person * p3 = [[Person alloc] initWithName:@"marray" andAge:38];

NSArray * array3 = [[NSArray alloc] initWithObjects:p1,p2,p3, nil];
NSLog(@"array3  %@",array3);

1.6 数组中存储基本数据类型

如果你要在数组中存储基本数据类型,请包装好了之后在去存 NSNumber

注意不要把nil值存储到 NSArray 中,会导致数据丢失

NSString * str = nil;
NSArray * array4 = [[NSArray alloc] initWithObjects:@"2",str,[NSNumber numberWithInt:23], nil];
NSLog(@"array4 %@",array4);

1.7 创建数组的快捷方式

NSArray * karray = @[@"a",@"b",@"c"];
NSLog(@"karray %@",karray);

1.8 快速获得一个数组中的元素

NSString * kstr = karray[0];
NSLog(@"kstr %@",kstr);

1.9 从数组中取出数据

NSArray * array = [[NSArray alloc] initWithObjects:@"one",@"two",@"three", nil];

数组的index时从0开始的

NSString * str = [array objectAtIndex:0];
NSLog(@"str %@",str);

1.10 获得数组的元素个数

NSUInteger arrayCount = [array count];
NSLog(@"arrayCount %d",arrayCount);

1.11 判断数组中是否存在某个对象

Person * p1 = [[Person alloc] initWithName:@"yofer" andAge:20];
Person * p2 = [[Person alloc] initWithName:@"luke" andAge:30];

NSArray * array = @[p1,p2];

BOOL isContain = [array containsObject:p1];
if (isContain)
{
    NSLog(@"存在");
}else
{
    NSLog(@"不存在");
}

2 遍历数组

2.1 使用for循环遍历数组

NSArray * array = @[@"one",@"two",@"three"];
for (int i = 0; i < array.count; i++)
{
    NSString * str = array[i];
    NSLog(@"array[%d] = %@",i,str);
}

2.2 使用增强for循环的方式遍历数组

for (NSString * str in array)
{
    NSLog(@"str = %@",str);
}

2.3 注意事项

如果你的数组中村粗了多种不同类型的数据,那么最好不要调用某个对象特有的方法,会导致程序崩溃

在实际的开发中,一个数组往往只负责存储一种数据类型

Person * p1 = [[Person alloc] init];
NSArray * array2 = @[@"one",[NSNumber numberWithInt:10],p1];

for (NSString * str in array2)
{
//    NSLog(@"array2 str %@",str);
//    NSLog(@"str %ld",str.length);
}

2.4 枚举器

NSArray * array3 = @[@"one",@"two",@"three"];

NSEnumerator * enumerateor =  [array3 objectEnumerator];

NSString * value;

while (value = [enumerateor nextObject]) {
    NSLog(@"enum str %@",value);
}

3 数组排序

3.1 使用 sortedArrayUsingSelector

是最简单的排序方式

数组是按照你存入元素的顺序存储的

NSArray * array = @[@"b",@"d",@"a",@"z"];
NSLog(@"排序前 array %@",array);

array = [array sortedArrayUsingSelector:@selector(compare:)];
// NSArray * array1 = [array sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"排序后 array %@",array);

3.2 使用block方式排序

NSArray * array2 = @[@"z",@"4",@"b",@"3",@"x"];

NSLog(@"array2 排序前 %@",array2);
array2 = [array2 sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {

    /*
    NSComparisonResult retuslt = [obj1 compare:obj2];

    return retuslt;
     */
    //obj1 compare obj2 就是正序排序
    //  return [obj1 compare:obj2];
    //obj2 compare obj1 就是倒序排序
    return [obj2 compare:obj1];

}];
NSLog(@"array2 排序后 %@",array2);

先给一个 Person 类

@interface Person : NSObject

@property (nonatomic,assign) int age;
@property (nonatomic,strong) NSString * name;
@property (nonatomic,strong) NSString * year;

- (id)initWithName:(NSString *)name  andAge:(int)age andYear:(NSString *)year;
//+ (id)personWithName:(NSString *)name  andAge:(int)age andYear:(NSString *)year;

@end

@implementation Person

- (id)initWithName:(NSString *)name  andAge:(int)age andYear:(NSString *)year
{
    if (self = [super init])
    {
        _name = name;
        _age = age;
        _year = year;

    }
    return self;
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"name %@ age %d year %@",_name,_age,_year];
}

@end

3.3 定制化数组对象排序

Person * p1 = [[Person alloc] initWithName:@"xiaozhe" andAge:20 andYear:@"1990"];
Person * p2 = [[Person alloc] initWithName:@"alex" andAge:18 andYear:@"2990"];
Person * p3 = [[Person alloc] initWithName:@"merry" andAge:25 andYear:@"1890"];

NSArray * array3 = @[p1,p2,p3];
NSLog(@"array3 排序前 %@",array3);

如果你向给你自己定义的对象排序,必须根据某一个属性来排序,

sortDescriptorWithKey 参数要的就是你对象中,要依据哪个属性来排序,你就把哪个属性的名字当成key传入

ascending YES表示正序 NO表示倒序

NSSortDescriptor * d1 = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];
NSSortDescriptor * d2 = [NSSortDescriptor sortDescriptorWithKey:@"year" ascending:NO];

如果你要使用多个属性进行排序,默认在前面的NSSortDescriptor优先级比较高

NSArray * descripts = @[d2,d1];

array3 = [array3 sortedArrayUsingDescriptors:descripts];

NSLog(@"array 3 排序后  %@",array3);

结果:

2016-08-14 14:13:57.238 05-数组排序[2105:516991] array3 排序前 (
    "name xiaozhe age 20 year 1990",
    "name alex age 18 year 2990",
    "name merry age 25 year 1890"
)
2016-08-14 14:13:57.238 05-数组排序[2105:516991] array 3 排序后  (
    "name alex age 18 year 2990",
    "name xiaozhe age 20 year 1990",
    "name merry age 25 year 1890"
)

再次测试:

NSArray * array4 = @[p1,p2,p3];
NSLog(@"array4 排序前 %@",array4);
array4 = [array4 sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {

    Person * p1 = obj1;
    Person * p2 = obj2;

    //year
    return [p1.year compare:p2.year];
}];
NSLog(@"array4 排序后 %@",array4);

结果:

2016-08-14 14:13:57.238 05-数组排序[2105:516991] array4 排序前 (
    "name xiaozhe age 20 year 1990",
    "name alex age 18 year 2990",
    "name merry age 25 year 1890"
)
2016-08-14 14:13:57.238 05-数组排序[2105:516991] array4 排序后 (
    "name merry age 25 year 1890",
    "name xiaozhe age 20 year 1990",
    "name alex age 18 year 2990"
)

4 可变数组: NSMutableArray

NSMutableArray 继承至 NSArray

4.1 创建可变数组

NSMutableArray * array = [[NSMutableArray alloc] initWithCapacity:0];

使用addObject 动态给数组中增加元素

[array addObject:@"one"];
[array addObject:@"two"];
[array addObject:@"three"];
[array addObject:@"one"];

NSString * str1 = @"one";
NSString * str2 = @"two";
NSString * str3 = @"three";

数组中可以存储,同一个对象多次

[array addObject:str1];
[array addObject:str2];
[array addObject:str3];
[array addObject:str1];

4.2 指定对象插入的位置

[array insertObject:str1 atIndex:2];

4.3 删除 会通过对象,删除数组中所有的同一个地址的对象

[array removeObject:str1];

4.4 通过索引的方式删除对象,超出了数组的count值,那么就会导致异常 index beyond bounds

[array removeObjectAtIndex:0];

[array addObject:str2];
[array addObject:str3];
[array addObject:str1];

4.5 删除数组中所有的元素

[array removeAllObjects];

NSLog(@"array %@",array);

[array addObject:str2];
[array addObject:str3];
[array addObject:str1];

5 可变数组 NSMutableArray 遍历

5.1 for换遍历

for (int i = 0; i < array.count; i++)
{
    NSString * str = [array objectAtIndex:i];

    //在遍历数组的时候,千万不要给数组中,增加,或者删除元素
    // [array removeObject:str1];

    NSLog(@"str %@",str);

}

5.2 增强for循环

for (NSString * str in array)
{
//    [array removeObject:str1];
    //如果你在增强for循环中,删除元素,会直接导致异常出现
    NSLog(@"str %@",str);
}

5.3 枚举器

NSEnumerator * enumerator = [array objectEnumerator];
NSString * value;
while (value = [enumerator nextObject]) {
    //如果你在枚举器中,删除元素,会直接导致异常出现
//    [array removeObject:str1];
    NSLog(@"str %@",value);
}

5.4 要通过遍历的方式确定删除哪个元素怎么办

NSMutableArray * array2 = [[NSMutableArray alloc] init];
[array2 addObject:@"1"];
[array2 addObject:@"2"];
[array2 addObject:@"3"];
[array2 addObject:@"4"];
[array2 addObject:@"5"];

NSMutableArray * tmp = [[NSMutableArray alloc] init];

for (NSString * str in array2)
{
    if ([str isEqualToString:@"3"])
    {
        [tmp addObject:str];
    }
}

NSLog(@"array2 %@",array2);
NSLog(@"tmp %@",tmp);

结果:

2016-08-14 14:35:20.437 06-可变数组[2252:538261] array2 (
    1,
    2,
    3,
    4,
    5
)
2016-08-14 14:35:20.437 06-可变数组[2252:538261] tmp (
    3
)

遍历临时数组

for (int i = 0; i < tmp.count; i++)
{
    NSString * str = [tmp objectAtIndex:i];

    //从原数组中删除,临时数组中存储的对象
    [array2 removeObject:str];
}

NSLog(@"array2 %@",array2);

新博客文章地址:NSArray/NSMutableArray创建,获取,遍历,排序

作者:zyq522376829 发表于2016/8/14 15:08:09 原文链接
阅读:30 评论:0 查看评论

智能厨房重构-使用Bmob后端云实现用户注册登录的功能。

$
0
0

上一篇智能厨房重构-用ActiveAndroid来实现收藏的功能介绍了本地数据库的实现,有了第一个博友评论说还不错,受益良多,很感动,因为这让我感觉到了技术分享的力量,我们都不是一个人孤军奋战,我们是一个互相交流的群体,因为分享,我们会变得更好。今天给大家介绍一下使用Bmob后端云搭建自己的网上服务器,以后再也不要求着搞服务器的了,自己一个人搞定一个客户端。因为此处篇幅比较多,所以我决定分两篇博客进行讲解,欢迎看下篇 智能厨房重构-使用Bmob后端云实现朋友圈的功能。

1. 环境配置

1.1 服务器配置

首先你需要注册一个Bmob的账号,进入官网即可,注册一个自己的App,如何在后台建立属于自己的表,基本都是傻瓜式建表了,不需要使用SQL语言,来看一下我建的表:

这里写图片描述

表搭建好了,服务器这边就完事了,就是如此简单。

1.2 客户端环境配置

搞完服务器,接下来就需要将bmob集成到我们的客户端中来了,其实我也是官网文档的搬运工,我就将最主要的步骤介绍一下:

这里写图片描述

去官网上面下载这两个JAR包,然后再build gradle中添加两个依赖,官网文档中还有直接导入的方式,给个链接http://docs.bmob.cn/data/Android/a_faststart/doc/index.html#初始化BmobSDK,都是比较简单的。

1.3 基本的数据类型和函数

数据类型
目前为止,Bmob支持的数据类型:String、Integer、Float、Short、Byte、Double、Character、Boolean、Object、Array。
同时也支持BmobObject、BmobDate、BmobGeoPoint、BmobFile特有的数据类型。

这里写图片描述

添加数据
添加数据使用BmobObject对象的save方法,就可以将当前对象的内容保存到Bmob服务端。
例如,你现在要保存一条游戏分数的记录,代码如下:

GameScore gameScore = new GameScore();
//注意:不能调用gameScore.setObjectId("")方法
gameScore.setPlayerName("比目");
gameScore.setScore(89);
gameScore.setIsPay(false);
gameScore.save(new SaveListener<String>() {

    @Override
    public void done(String objectId, BmobException e) {
        if(e==null){
            toast("创建数据成功:" + objectId);
        }else{
            Log.i("bmob","失败:"+e.getMessage()+","+e.getErrorCode());
        }
    }
})

删除数据
从服务器删除对象。例如:将GameScore表中objectId为dd8e6aff28的数据删除。

GameScore gameScore = new GameScore();
gameScore.setObjectId("dd8e6aff28");
gameScore.delete(new UpdateListener() {

    @Override
    public void done(BmobException e) {
        if(e==null){
            Log.i("bmob","成功");
        }else{
            Log.i("bmob","失败:"+e.getMessage()+","+e.getErrorCode());
        }
    }
});

查询数据
当我们知道某条数据的objectId时,就可以根据objectId直接获取单条数据对象。例如:查询objectId为a203eba875的人员信息。

BmobQuery<GameScore> query = new BmobQuery<GameScore>();
query.getObject("a203eba875", new QueryListener<GameScore>() {

    @Override
    public void done(GameScore object, BmobException e) {
        if(e==null){
            //获得playerName的信息
            object.getPlayerName();
            //获得数据的objectId信息
            object.getObjectId();
            //获得createdAt数据创建时间(注意是:createdAt,不是createAt)
            object.getCreatedAt();
        }else{
            Log.i("bmob","失败:"+e.getMessage()+","+e.getErrorCode());
        }
    }

});

修改数据
更新一个对象也是非常简单。例如:将GameScore表中objectId为0c6db13c的游戏分数修改为77.

GameScore gameScore = new GameScore();
gameScore.setScore(77);
gameScore.update("0c6db13c", new UpdateListener() {

    @Override
    public void done(BmobException e) {
        if(e==null){
            Log.i("bmob","更新成功");
        }else{
            Log.i("bmob","更新失败:"+e.getMessage()+","+e.getErrorCode());
        }
    }
});

函数都比较简单好用,如果想要了解更多,请直接看官网文档。

2.实战

现在服务器和客户端的环境都配置好,现在我们可以开始实战了。我们要实现的注册和登录的功能,首先来看一下流程
这里写图片描述

2.1 建立实体对象

使用Bmob,必须遵守的一个规则就是,你的实体对象名必须和表名相同,表中的每一个列都对应一个属性,必须继承BmobObject这个类,这一点很不好,假如和ActiveAndroid连用的话,就冲突了,因为Java中不能多重继承,比如上面的用户表对应实体类就是:

/**
 * 作者:GXL on 2016/8/3 0003
 * 博客: http://blog.csdn.net/u014316462
 * 作用:用户表
 */
public class User extends BmobObject implements Serializable{

    //姓名
    String Name;
    //头像
    BmobFile Photo;
    //密码
    String Password;

    public String getNumber() {
        return Number;
    }

    public void setNumber(String number) {
        Number = number;
    }

    //手机号码
    String Number;

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public BmobFile getPhoto() {
        return Photo;
    }

    public void setPhoto(BmobFile photo) {
        Photo = photo;
    }

    public String getPassword() {
        return Password;
    }

    public void setPassword(String password) {
        Password = password;
    }

    public User() {
    }
}

2.2 建立UserModel和服务器进行数据交互

/**
 * 作者:GXL on 2016/8/3 0003
 * 博客: http://blog.csdn.net/u014316462
 * 作用:用户信息Model
 */
public class UserModel implements UserModelImpl {

    private final String LOGINUSER = "loginuser";

    /**
     * 用户登录验证
     *
     * @param phone
     * @param passoword
     * @param listener
     */
    @Override
    public void getUser(String phone, String passoword, final FoodModelImpl.BaseListener listener) {
        BmobQuery<User> query = new BmobQuery<User>();
        query.addWhereEqualTo("Number", phone);
        query.addWhereEqualTo("Password", passoword);
        query.setLimit(1);
        query.findObjects(BaseApplication.getmContext(), new FindListener<User>() {
            @Override
            public void onSuccess(List<User> object) {
                if (object != null && object.size() != 0) {
                    SPUtils.put(BaseApplication.getmContext(), LOGINUSER, object.get(0));
                    listener.getSuccess(object.get(0));
                } else {
                    listener.getFailure();
                }
            }

            @Override
            public void onError(int code, String msg) {

            }
        });
    }

    /**
     * 根据objectId获取User
     *
     * @param objectId
     * @param listener
     */
    @Override
    public void getUser(String objectId, final FoodModelImpl.BaseListener listener) {
        BmobQuery<User> query = new BmobQuery<User>();
        query.addWhereEqualTo("objectId", objectId);
        query.setLimit(1);
        query.findObjects(BaseApplication.getmContext(), new FindListener<User>() {
            @Override
            public void onSuccess(List<User> object) {
                if (object != null && object.size() != 0) {
                    SPUtils.put(BaseApplication.getmContext(), LOGINUSER, object.get(0));
                    listener.getSuccess(object.get(0));
                } else {
                    listener.getFailure();
                }
            }

            @Override
            public void onError(int code, String msg) {

            }
        });
    }

    /**
     * 更换用户的头像
     *
     * @param path
     * @param listener
     */
    public void updateUserPhoto(String path, final String objectId, final FoodModelImpl.BaseListener listener) {
        final BmobFile bmobFile = new BmobFile(new File(path));
        bmobFile.upload(BaseApplication.getmContext(), new UploadFileListener() {
            @Override
            public void onSuccess() {
                final User user = new User();
                user.setPhoto(bmobFile);
                user.update(BaseApplication.getmContext(), objectId, new UpdateListener() {
                    @Override
                    public void onSuccess() {
                        listener.getSuccess(user);

                    }

                    @Override
                    public void onFailure(int i, String s) {
                        listener.getFailure();
                    }
                });
            }

            @Override
            public void onFailure(int i, String s) {
                listener.getFailure();
            }
        });
    }

    /**
     * 判断当前用户是否登录
     *
     * @return
     */
    public boolean isLogin() {
        List<UserLocal> list = new Select().from(UserLocal.class).execute();
        if (list.size() != 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 将当前登录的对象保持到数据库中
     *
     * @param userLocal
     */
    public void putUserLocal(UserLocal userLocal) {
        new Delete().from(UserLocal.class).execute();
        userLocal.save();
    }

    /**
     * 获取当前登录的对象
     *
     * @return
     */
    public UserLocal getUserLocal() {
        return new Select().from(UserLocal.class).executeSingle();
    }


    /**
     * 注册功能
     *
     * @param user
     */
    public void onRegister(User user, final FoodModelImpl.BaseListener listener) {
        user.save(BaseApplication.getmContext(), new SaveListener() {
            @Override
            public void onSuccess() {
                ToastUtils.showLong(BaseApplication.getmContext(), "注册成功");
                listener.getSuccess(null);
            }

            @Override
            public void onFailure(int i, String s) {
                ToastUtils.showLong(BaseApplication.getmContext(), "注册失败");
                listener.getFailure();
            }
        });
    }

    /**
     * 判断当前手机号码是否注册
     *
     * @param phone
     * @param listener
     * @return
     */
    public void isPhoneRegister(String phone, final FoodModelImpl.BaseListener listener) {
        BmobQuery<User> query = new BmobQuery<User>();
        query.addWhereEqualTo("Number", phone);
        query.setLimit(1);
        query.findObjects(BaseApplication.getmContext(), new FindListener<User>() {
            @Override
            public void onSuccess(List<User> object) {
                if (object != null && object.size() != 0) {
                    listener.getSuccess(object.get(0));
                } else {
                    listener.getFailure();
                }
            }

            @Override
            public void onError(int code, String msg) {

            }
        });
    }
}

所有和服务器的数据请求和提交都在上面了,都很简单,都是利用了上面的函数。

2.3 使用效果

这里写图片描述

详细的项目代码地址:https://github.com/gxl1240779189/ReIntelligentKitchen,如果决定效果还可以,请点个赞支持一下,多谢了。

作者:u014316462 发表于2016/8/14 15:12:17 原文链接
阅读:10 评论:0 查看评论

Android 蓝牙音箱开发

$
0
0

项目下载地址github

1.打开蓝牙:

      mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

      /**如果本地蓝牙没有开启,则开启*/
      if (!mBluetoothAdapter.isEnabled()) {
        // 我们通过startActivityForResult()方法发起的Intent将会在onActivityResult()回调方法中获取用户的选择,比如用户单击了Yes开启,
        // 那么将会收到RESULT_OK的结果,
        // 如果RESULT_CANCELED则代表用户不愿意开启蓝牙
        Intent mIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(mIntent, ENABLE_BLUE);
       } else {
           Toast.makeText(this, "蓝牙已开启", Toast.LENGTH_SHORT).show();
       }

监听打开的结果:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == ENABLE_BLUE) {
            if (resultCode == RESULT_OK) {
                Toast.makeText(this, "蓝牙开启成功", Toast.LENGTH_SHORT).show();
                getBondedDevices();
            } else if (resultCode == RESULT_CANCELED) {
                Toast.makeText(this, "蓝牙开始失败", Toast.LENGTH_SHORT).show();
            }
        } else {

        }
    }

2.关闭蓝牙:

     /**关闭蓝牙*/
     if (mBluetoothAdapter.isEnabled()) {
           mBluetoothAdapter.disable();
     }

3.设计蓝牙为可见:

     Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
     intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 180);//180可见时间
     startActivity(intent);

4.收索蓝牙:

注册广播监听搜索的结果:

     /**注册搜索蓝牙receiver*/
     mFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
     mFilter.addAction(BluetoothDevice.ACTION_FOUND);
     mFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
     registerReceiver(mReceiver, mFilter);

开始的搜索:

     // 如果正在搜索,就先取消搜索
     if (mBluetoothAdapter.isDiscovering()) {
         mBluetoothAdapter.cancelDiscovery();
     }
     // 开始搜索蓝牙设备,搜索到的蓝牙设备通过广播返回
     mBluetoothAdapter.startDiscovery();

监听搜索的结果:

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            /** 搜索到的蓝牙设备*/
            if (action.equals(BluetoothDevice.ACTION_FOUND)) {
                BluetoothDevice device = intent
                        .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                // 搜索到的不是已经配对的蓝牙设备
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    BlueDevice blueDevice = new BlueDevice();
                    blueDevice.setName(device.getName());
                    blueDevice.setAddress(device.getAddress());
                    blueDevice.setDevice(device);
                    setDevices.add(blueDevice);
                    blueAdapter.setSetDevices(setDevices);
                    blueAdapter.notifyDataSetChanged();
                    Log.d(MAINACTIVITY, "搜索结果......"+device.getName());
                }

                /**当绑定的状态改变时*/
            } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {




                /**搜索完成*/
            } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                setProgressBarIndeterminateVisibility(false);
                Log.d(MAINACTIVITY, "搜索完成......");
                hideProgressDailog();
            }
        }
    };

5.配对蓝牙:

配对工具类

    public class BlueUtils {

        public BlueUtils(BlueDevice blueDevice) {
            this.blueDevice = blueDevice;
        }

        /**
         * 配对
         */
        public void doPair() {
                if(null == mOthHandler){
                    HandlerThread handlerThread = new HandlerThread("other_thread");
                    handlerThread.start();
                    mOthHandler = new Handler(handlerThread.getLooper());
                }
                mOthHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        initSocket();   //取得socket
                        try {
                            socket.connect();   //请求配对
        //                      mAdapterManager.updateDeviceAdapter();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
        }


        /**
         * 取消蓝牙配对
         * @param device
         */
        public static void unpairDevice(BluetoothDevice device) {
            try {
                Method m = device.getClass()
                        .getMethod("removeBond", (Class[]) null);
                m.invoke(device, (Object[]) null);
            } catch (Exception e) {
                Log.d("BlueUtils", e.getMessage());
            }
        }


        /**
         * 取得BluetoothSocket
         */
       private void initSocket() {
            BluetoothSocket temp = null;
            try {
                Method m = blueDevice.getDevice().getClass().getMethod("createRfcommSocket", new Class[] {int.class});
                temp = (BluetoothSocket) m.invoke(blueDevice.getDevice(), 1);
                //怪异错误: 直接赋值给socket,对socket操作可能出现异常,  要通过中间变量temp赋值给socket
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            socket = temp;
        }

    }

注册监听配对结果的广播(使用同上面的注册代码)

     /**注册搜索蓝牙receiver*/
     mFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
     mFilter.addAction(BluetoothDevice.ACTION_FOUND);
     mFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
     registerReceiver(mReceiver, mFilter);

开始配对

    /**
     * 开始配对蓝牙设备
     *
     * @param blueDevice
     */
    private void startPariBlue(BlueDevice blueDevice) {
        BlueUtils blueUtils = new BlueUtils(blueDevice);
        blueUtils.doPair();
    }

监听配对结果:(使用同上面的广播接收者)

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            /** 搜索到的蓝牙设备*/
            if (action.equals(BluetoothDevice.ACTION_FOUND)) {

                .....
                /**当绑定的状态改变时*/
            } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                switch (device.getBondState()) {
                    case BluetoothDevice.BOND_BONDING:
                        Log.d(MAINACTIVITY, "正在配对......");

                        break;
                    case BluetoothDevice.BOND_BONDED:
                        Log.d(MAINACTIVITY, "完成配对");
                        hideProgressDailog();
                        /**开始连接*/
                        contectBuleDevices();
                        break;
                    case BluetoothDevice.BOND_NONE:
                        Log.d(MAINACTIVITY, "取消配对");
                        Toast.makeText(MainActivity.this,"成功取消配对",Toast.LENGTH_SHORT).show();
                        getBondedDevices();
                        break;
                    default:
                        break;
                }

                /**搜索完成*/
            } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                ....
            }
        }
    };

6.使用A2DP协议连接蓝牙设备:

连接设备


    /**
     * 开始连接蓝牙设备
     */
    private void contectBuleDevices() {
        /**使用A2DP协议连接设备*/
        mBluetoothAdapter.getProfileProxy(this, mProfileServiceListener, BluetoothProfile.A2DP);
    }

监听连接的回调

    /**
     * 连接蓝牙设备(通过监听蓝牙协议的服务,在连接服务的时候使用BluetoothA2dp协议)
     */
    private BluetoothProfile.ServiceListener mProfileServiceListener = new BluetoothProfile.ServiceListener() {

        @Override
        public void onServiceDisconnected(int profile) {

        }

        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            try {
                if (profile == BluetoothProfile.HEADSET) {
                    ....

                } else if (profile == BluetoothProfile.A2DP) {
                    /**使用A2DP的协议连接蓝牙设备(使用了反射技术调用连接的方法)*/
                    a2dp = (BluetoothA2dp) proxy;
                    if (a2dp.getConnectionState(currentBluetoothDevice) != BluetoothProfile.STATE_CONNECTED) {
                        a2dp.getClass()
                                .getMethod("connect", BluetoothDevice.class)
                                .invoke(a2dp, currentBluetoothDevice);
                        Toast.makeText(MainActivity.this,"请播放音乐",Toast.LENGTH_SHORT).show();
                        getBondedDevices();
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

7.添加权限

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

8.打开乐库播放音乐

9.Android 6.0的系统需要动态添加权限才能搜索出蓝牙设备

Android 6.0的系统需要动态添加权限

    /**判断手机系统的版本*/
    if (Build.VERSION.SDK_INT >= 6.0) {//Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
            if(ActivityCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION)!=PackageManager.PERMISSION_GRANTED){
                /**动态添加权限:ACCESS_FINE_LOCATION*/
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        MY_PERMISSION_REQUEST_CONSTANT);
            }
        }

请求权限的回调

    /**请求权限的回调:这里判断权限是否添加成功*/
     /**请求权限的回调:这里判断权限是否添加成功*/
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSION_REQUEST_CONSTANT: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.i("main","添加权限成功");
                }
                return;
            }
        }
    }
作者:u012987546 发表于2016/8/14 15:16:48 原文链接
阅读:37 评论:0 查看评论

android图片三级缓存

$
0
0

什么是图片的三级缓存

  • 1、内存缓存 优先加载,速度最快
  • 2、本地缓存 次优先加载 速度稍快
  • 3、网络缓存 最后加载 速度由网络速度决定(浪费流量)


图片缓存原理
内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存,其实网络不算Cache。当根据url向网络加载图片的时候,先从内存中找,如果内存中没有,再从缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求加载图片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是Bitmap。所以,按照这个逻辑,只要一个url被下载过,其实图片就被缓存起来了。

LruCache

内存缓存技术对那些大量占用应用程序宝贵内存的图片提供了快速访问的方法。其中最核心的类是LruCache(此类在android-support-v4的包中提供。这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。

DiskLruCache

LruCache只是管理了内存中图片的存储与释放,如果图片从内存中被移除的话,那么又需要从网络上重新加载一次图片,这显然非常耗时。对此,Google又提供了一套硬盘缓存的解决方案:DiskLruCache(Google官方编写,但获得官方认证)。只可惜,Android Doc中并没有对DiskLruCache的用法给出详细的说明,而网上关于DiskLruCache的资料也少之又少。


图片的三级缓存图形解释


这里写图片描述

好了,既然知道了什么三级缓存的字面意思了,那么我们就来处理吧。

图片缓存—内存缓存的原理

MemoryCacheUtils.java:

<code class="hljs java has-numbering" style="margin: 8px 0px; display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> com.example.bitmaputils.bitmap;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.graphics.Bitmap;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.util.Log;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.util.LruCache;

<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * 内存缓存
 */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MemoryCacheUtils</span> {</span><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * LinkedHashMap<>(10,0.75f,true);
     * <p/>
     * 10是最大致   0.75f是加载因子   true是访问排序   false插入排序
     *
     *
     */</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//private LinkedHashMap<String,Bitmap> mMemoryCache = new LinkedHashMap<>(5,0.75f,true);</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> LruCache<String, Bitmap> mLruCache;


    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">MemoryCacheUtils</span>() {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> maxMemory = Runtime.getRuntime().maxMemory();<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//最大内存  默认是16兆  运行时候的</span>
        mLruCache = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> LruCache<String, Bitmap>((<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>) (maxMemory / <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8</span>)) {
            <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">sizeOf</span>(String key, Bitmap value) {
                <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//int byteCount = value.getByteCount();</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//得到图片字节数</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// @return number of bytes between rows of the native bitmap pixels.</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> byteCount = value.getRowBytes() * value.getWidth();
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> byteCount;
            }
        };
    }

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 从内存中读取
     *
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> url
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Bitmap <span class="hljs-title" style="box-sizing: border-box;">getFromMemroy</span>(String url) {

        Log.d(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MyBitmapUtils"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"从内存中加载图片"</span>);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> mLruCache.get(url);
    }

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 写入到内存中
     *
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> url
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> bitmap
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">setToMemory</span>(String url, Bitmap bitmap) {
        mLruCache.put(url, bitmap);
    }
}
</code><ul class="pre-numbering" style="margin: 0px; padding: 6px 0px 40px; box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">1</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">2</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">3</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">4</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">5</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">6</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">7</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">8</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">9</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">10</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">11</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">12</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">13</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">14</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">15</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">16</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">17</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">18</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">19</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">20</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">21</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">22</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">23</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">24</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">25</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">26</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">27</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">28</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">29</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">30</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">31</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">32</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">33</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">34</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">35</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">36</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">37</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">38</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">39</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">40</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">41</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">42</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">43</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">44</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">45</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">46</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">47</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">48</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">49</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">50</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">51</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">52</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">53</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">54</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">55</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">56</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">57</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">58</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">59</li></ul>

很简单吧,在这里我们使用了LruCache(),对图片进行了内存缓存,这里我们只是稍微进行了处理,在这里我们只是介绍原理,当然了哈,性能,OOM溢出问题我们在这里不作处理。

图片缓存—本地缓存

<code class="hljs java has-numbering" style="margin: 8px 0px; display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> com.example.bitmaputils.bitmap;


<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.graphics.Bitmap;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.graphics.BitmapFactory;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.os.Environment;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.util.Log;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> com.example.bitmaputils.MD5Encoder;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.io.File;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.io.FileInputStream;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.io.FileNotFoundException;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.io.FileOutputStream;

<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * 本地缓存
 */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">SDcardCacheUtils</span> {</span><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 我们读取内存的绝对路径
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">final</span> String CACHE_PATH = Environment
            .getExternalStorageDirectory().getAbsolutePath() + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"/aixuexi"</span>;

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 从本地读取
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> url
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Bitmap <span class="hljs-title" style="box-sizing: border-box;">getFromSd</span>(String url){
        String fileName = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//得到图片的url的md5的文件名</span>
            fileName = MD5Encoder.encode(url);
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) {
            e.printStackTrace();
        }
        File file = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(CACHE_PATH,fileName);

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//如果存在,就通过bitmap工厂,返回的bitmap,然后返回bitmap</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (file.exists()){
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
                Bitmap bitmap = BitmapFactory.decodeStream(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> FileInputStream(file));
                Log.d(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MyBitmapUtils"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"从本地读取图片啊"</span>);
                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> bitmap;
            } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
    }

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 向本地缓存
     *
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> url   图片地址
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> bitmap   图片
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">savaSd</span>(String url,Bitmap bitmap){
        String fileName = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//我们对图片的地址进行MD5加密,作为文件名</span>
            fileName = MD5Encoder.encode(url);
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (Exception e) {
            e.printStackTrace();
        }

        <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
         * 以CACHE_PATH为文件夹  fileName为文件名
         */</span>
        File file = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> File(CACHE_PATH,fileName);

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//我们首先得到他的符文剑</span>
        File parentFile = file.getParentFile();
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//查看是否存在,如果不存在就创建</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!parentFile.exists()){
            parentFile.mkdirs(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//创建文件夹</span>
        }

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//将图片保存到本地</span><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
             *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> format   The format of the compressed image   图片的保存格式
             *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> quality  Hint to the compressor, 0-100. 0 meaning compress for
             *                 small size, 100 meaning compress for max quality. Some
             *                 formats, like PNG which is lossless, will ignore the
             *                 quality setting
             *                 图片的保存的质量    100最好
             *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> stream   The outputstream to write the compressed data.
             */</span>
            bitmap.compress(Bitmap.CompressFormat.JPEG,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">100</span>,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> FileOutputStream(file));
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

</code><ul class="pre-numbering" style="margin: 0px; padding: 6px 0px 40px; box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">1</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">2</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">3</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">4</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">5</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">6</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">7</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">8</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">9</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">10</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">11</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">12</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">13</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">14</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">15</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">16</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">17</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">18</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">19</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">20</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">21</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">22</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">23</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">24</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">25</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">26</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">27</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">28</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">29</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">30</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">31</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">32</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">33</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">34</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">35</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">36</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">37</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">38</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">39</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">40</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">41</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">42</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">43</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">44</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">45</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">46</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">47</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">48</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">49</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">50</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">51</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">52</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">53</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">54</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">55</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">56</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">57</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">58</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">59</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">60</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">61</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">62</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">63</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">64</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">65</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">66</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">67</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">68</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">69</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">70</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">71</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">72</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">73</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">74</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">75</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">76</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">77</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">78</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">79</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">80</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">81</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">82</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">83</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">84</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">85</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">86</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">87</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">88</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">89</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">90</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">91</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">92</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">93</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">94</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">95</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">96</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">97</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">98</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">99</li></ul>

图片缓存—网络缓存

NetCacheUtils .java:

<code class="hljs java has-numbering" style="margin: 8px 0px; display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> com.example.bitmaputils.bitmap;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.graphics.Bitmap;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.graphics.BitmapFactory;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.os.AsyncTask;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.util.Log;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.widget.ImageView;

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.io.IOException;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.io.InputStream;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.net.HttpURLConnection;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> java.net.URL;

<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * 网络缓存工具类
 */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">NetCacheUtils</span> {</span><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 图片
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> ImageView mImageView;

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 图片地址
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> String mUrl;

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 本地缓存
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> SDcardCacheUtils mDcardCacheUtils;


    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 内存缓存
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> MemoryCacheUtils mMemoryCacheUtils;


    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">NetCacheUtils</span>(SDcardCacheUtils dcardCacheUtils, MemoryCacheUtils memoryCacheUtils) {
        mDcardCacheUtils = dcardCacheUtils;
        mMemoryCacheUtils = memoryCacheUtils;
    }

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 从网络中下载图片
     *
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> image
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> url
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">getDataFromNet</span>(ImageView image, String url) {
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> MyAsyncTask().execute(image, url);  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//启动Asynctask,传入的参数到对应doInBackground()</span>
    }


    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 异步下载
     * <p/>
     * 第一个泛型 : 参数类型  对应doInBackground()
     * 第二个泛型 : 更新进度   对应onProgressUpdate()
     * 第三个泛型 : 返回结果result   对应onPostExecute
     */</span>
    class MyAsyncTask extends AsyncTask<Object, Void, Bitmap> {

        <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
         * 后台下载  子线程
         *
         *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> params
         *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</span>
         */</span><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> Bitmap <span class="hljs-title" style="box-sizing: border-box;">doInBackground</span>(Object... params) {

            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//拿到传入的image</span>
            mImageView = (ImageView) params[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>];

            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//得到图片的地址</span>
            mUrl = (String) params[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>];
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//将imageview和url绑定,防止错乱</span>
            mImageView.setTag(mUrl);

            Bitmap bitmap = downLoadBitmap(mUrl);

            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> bitmap;
        }


        <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
         * 进度更新   UI线程
         *
         *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> values
         */</span><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onProgressUpdate</span>(Void... values) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onProgressUpdate(values);
        }

        <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
         * 回调结果,耗时方法结束后,主线程
         *
         *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> bitmap
         */</span><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onPostExecute</span>(Bitmap bitmap) {
            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (bitmap != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
                <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//得到图片的tag值</span>
                String url = (String) mImageView.getTag();
                <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//确保图片设置给了正确的image</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (url.equals(mUrl)) {
                    mImageView.setImageBitmap(bitmap);

                    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
                     * 当从网络上下载好之后保存到sdcard中
                     */</span>
                    mDcardCacheUtils.savaSd(mUrl, bitmap);

                    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
                     *  写入到内存中
                     */</span>
                    mMemoryCacheUtils.setToMemory(mUrl, bitmap);
                    Log.d(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"MyBitmapUtils"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"我是从网络缓存中读取的图片啊"</span>);
                }
            }
        }
    }

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 下载图片
     *
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> url 下载图片地址
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @return</span>
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Bitmap <span class="hljs-title" style="box-sizing: border-box;">downLoadBitmap</span>(String url) {

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//连接</span>
        HttpURLConnection conn = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
            conn = (HttpURLConnection) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> URL(url)
                    .openConnection();

            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//设置读取超时</span>
            conn.setReadTimeout(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5000</span>);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//设置请求方法</span>
            conn.setRequestMethod(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"GET"</span>);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//设置连接超时连接</span>
            conn.setConnectTimeout(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">5000</span>);
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//连接</span>
            conn.connect();

            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//响应码</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> code = conn.getResponseCode();

            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (code == <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">200</span>) {  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//请求正确的响应码是200</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//得到响应流</span>
                InputStream inputStream = conn.getInputStream();
                <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//得到bitmap对象</span>
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> bitmap;
            }
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (IOException e) {
 #
          e.printStackTrace();
        } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">finally</span> {
            conn.disconnect();
        }

        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
    }
}
</code><ul class="pre-numbering" style="margin: 0px; padding: 6px 0px 40px; box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">1</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">2</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">3</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">4</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">5</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">6</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">7</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">8</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">9</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">10</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">11</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">12</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">13</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">14</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">15</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">16</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">17</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">18</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">19</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">20</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">21</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">22</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">23</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">24</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">25</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">26</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">27</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">28</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">29</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">30</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">31</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">32</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">33</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">34</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">35</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">36</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">37</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">38</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">39</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">40</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">41</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">42</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">43</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">44</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">45</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">46</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">47</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">48</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">49</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">50</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">51</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">52</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">53</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">54</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">55</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">56</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">57</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">58</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">59</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">60</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">61</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">62</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">63</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">64</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">65</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">66</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">67</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">68</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">69</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">70</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">71</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">72</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">73</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">74</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">75</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">76</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">77</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">78</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">79</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">80</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">81</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">82</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">83</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">84</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">85</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">86</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">87</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">88</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">89</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">90</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">91</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">92</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">93</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">94</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">95</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">96</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">97</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">98</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">99</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">100</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">101</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">102</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">103</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">104</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">105</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">106</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">107</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">108</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">109</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">110</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">111</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">112</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">113</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">114</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">115</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">116</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">117</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">118</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">119</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">120</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">121</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">122</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">123</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">124</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">125</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">126</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">127</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">128</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">129</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">130</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">131</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">132</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">133</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">134</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">135</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">136</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">137</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">138</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">139</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">140</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">141</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">142</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">143</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">144</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">145</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">146</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">147</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">148</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">149</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">150</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">151</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">152</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">153</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">154</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">155</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">156</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">157</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">158</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">159</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">160</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">161</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">162</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">163</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">164</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">165</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">166</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">167</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">168</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">169</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">170</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">171</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">172</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">173</li></ul>

好了,至此,一个简单的图片的三级缓存完成了,接下来看看使用吧。

图片加载工具类

MyBitmapUtils .java:

<code class="hljs java has-numbering" style="margin: 8px 0px; display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> com.example.bitmaputils.bitmap;

<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * Created by 若兰 on 2016/1/29.
 * 一个懂得了编程乐趣的小白,希望自己
 * 能够在这个道路上走的很远,也希望自己学习到的
 * 知识可以帮助更多的人,分享就是学习的一种乐趣
 * QQ:1069584784
 * csdn:http://blog.csdn.net/wuyinlei
 */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.graphics.Bitmap;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.widget.ImageView;

<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * 自定义的bitmap工具类
 */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MyBitmapUtils</span> {</span><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 网络缓存
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> NetCacheUtils mNetCacheUtils;

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 本地缓存
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> SDcardCacheUtils mSdCacheUtils;

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 内存缓存
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> MemoryCacheUtils mMemoryCacheUtils;


    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">MyBitmapUtils</span>() {
        mSdCacheUtils = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> SDcardCacheUtils();
        mMemoryCacheUtils = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> MemoryCacheUtils();
        mNetCacheUtils = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> NetCacheUtils(mSdCacheUtils, mMemoryCacheUtils);
    }

    <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
     * 展示图片的方法
     *
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> image
     *<span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> url
     */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">display</span>(ImageView image, String url) {


        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//从内存中读取</span>
        Bitmap fromMemroy = mMemoryCacheUtils.getFromMemroy(url);
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//如果内存中有的h话就直接返回,从内存中读取</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (fromMemroy != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            image.setImageBitmap(fromMemroy);

            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
        }


        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//从本地SD卡读取</span>
        Bitmap fromSd = mSdCacheUtils.getFromSd(url);
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (fromSd != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>) {
            image.setImageBitmap(fromSd);

            mMemoryCacheUtils.setToMemory(url, fromSd);

            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;
        }
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//从网络中读取</span>
        mNetCacheUtils.getDataFromNet(image, url);

    }
}
</code><ul class="pre-numbering" style="margin: 0px; padding: 6px 0px 40px; box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">1</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">2</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">3</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">4</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">5</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">6</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">7</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">8</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">9</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">10</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">11</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">12</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">13</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">14</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">15</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">16</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">17</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">18</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">19</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">20</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">21</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">22</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">23</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">24</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">25</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">26</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">27</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">28</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">29</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">30</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">31</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">32</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">33</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">34</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">35</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">36</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">37</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">38</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">39</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">40</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">41</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">42</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">43</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">44</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">45</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">46</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">47</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">48</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">49</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">50</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">51</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">52</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">53</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">54</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">55</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">56</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">57</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">58</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">59</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">60</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">61</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">62</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">63</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">64</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">65</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">66</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">67</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">68</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">69</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">70</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">71</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">72</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">73</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">74</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">75</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">76</li></ul>

使用这个工具类就很简单了 
只需在加载数据的适配器中

<code class="hljs cs has-numbering" style="margin: 8px 0px; display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//声明图片加载工具类</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> MyBitmapUtils utils;
        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-title" style="box-sizing: border-box;">PhotoAdapter</span>() {
            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//mBitmapUtils = new BitmapUtils(MainActivity.this);</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// mBitmapUtils.configDefaultLoadingImage(R.mipmap.defaut);</span>
            utils = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> MyBitmapUtils();
        }

然后在getView()方法中,使用工具类中的display()方法就可以了。简单吧
utils.display(holder.tvImage,mImageViews[position]);</code><ul class="pre-numbering" style="margin: 0px; padding: 6px 0px 40px; box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">1</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">2</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">3</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">4</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">5</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">6</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">7</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">8</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">9</li><li style="margin: 0px; box-sizing: border-box; padding: 0px 5px;">10</li></ul>


作者:ApplePear1024 发表于2016/8/14 16:05:25 原文链接
阅读:35 评论:0 查看评论

Android GridView如何适配不同屏幕

$
0
0

Android GridView如何适配不同屏幕

GridView和ListView一样,都是项目中常用的控件之一,针对Android市场层出不穷的手机机型,在实际开发中,我们就需要去做适配,这篇文章就给大家讲解一下GridView的适配。

效果图:

GridView适配效果图

GridView.xml

<GridView
        android:id="@+id/gridview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/transparent"
        android:descendantFocusability="blocksDescendants"
        android:gravity="center"
        android:horizontalSpacing="3dp"
        android:numColumns="3"
        android:scrollbars="none"
        android:verticalSpacing="3dp"

        />

android:numColumns=”3” 一行三个
android:horizontalSpacing=”3dp” 子控件item之间的行间距及列间距
android:verticalSpacing=”3dp”

Gridview与ListView用法原理是一样的都需要用到Adapter,一般都是继承BaseAdapter。

接下来介绍在Adapter如何用GridView去做适配,直接上代码:

  //宽度高度适配  
            WindowManager wm = MomentNewsActivity.this.getWindowManager();
            int width = wm.getDefaultDisplay().getWidth();
            AbsListView.LayoutParams layoutParams = new AbsListView.LayoutParams(width/3, width/3);
            convertView.setLayoutParams(layoutParams);

上面这段代码写在return convertView之前即可。

Gridview的简单适配就是这样的,接下来介绍一下Gridview稍微复杂一些的适配,主要的就是适配是要显示的主要内容要等比例的放大缩小来适应屏幕的分辨率。

先上效果图:
Gridview适配

这次每个展示的item不再是简单的正方形的展现形式了,而是长方形的展示。分析一下,item显示中的整体是长方形,照片展示还是采取正方形展示,我们只需要将长方形等比例的放大缩小,然后将照片展示的正方形采取正方形展示的适配,适配问题就解决了。

xml:

   <GridView
                    android:id="@+id/gridview_samecity"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:background="@color/transparent"
                    android:descendantFocusability="blocksDescendants"
                    android:gravity="center"
                    android:horizontalSpacing="3dp"
                    android:numColumns="2"
                    android:scrollbars="none"
                    android:verticalSpacing="3dp"

                    />

item的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:id="@+id/layout_the_same_city"
    android:layout_width="170dp"
    android:layout_height="233dp"
    android:background="@color/M323535"
    android:orientation="vertical">

    <!--//故事照片 -->
    <!--标题-->
    <RelativeLayout
        android:id="@+id/layout_story_pic"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp170">

        <ImageView
            android:id="@+id/img_bitmap"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:contentDescription="@null" />

        <RelativeLayout
            android:id="@+id/layout_story"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/M0D493E"
                android:contentDescription="@null"
                android:scaleType="centerCrop" />

            <ImageView
                android:id="@+id/img_story_bitmap"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginBottom="@dimen/dp5"
                android:layout_marginEnd="@dimen/dp5"
                android:layout_marginLeft="@dimen/dp5"
                android:layout_marginRight="@dimen/dp5"
                android:layout_marginStart="@dimen/dp5"
                android:layout_marginTop="@dimen/dp5"
                android:background="@mipmap/now_default_img"
                android:contentDescription="@null"
                android:scaleType="centerCrop" />

        </RelativeLayout>


        <ImageView
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp85"
            android:layout_alignParentBottom="true"
            android:background="@mipmap/story_cover"
            android:contentDescription="@null" />

        <TextView
            android:id="@+id/txt_story_title"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp26"
            android:layout_alignParentBottom="true"
            android:gravity="center|start"
            android:paddingEnd="@dimen/dp36"
            android:paddingLeft="@dimen/dp10"
            android:paddingRight="@dimen/dp36"
            android:paddingStart="@dimen/dp10"
            android:singleLine="true"
            android:textColor="@color/ME6E6E6"
            android:textSize="@dimen/sp12" />


        <!--//视频-->
        <ImageView
            android:id="@+id/img_video"
            android:layout_width="@dimen/dp35"
            android:layout_height="@dimen/dp35"
            android:layout_centerInParent="true"
            android:background="@mipmap/video_play"
            android:contentDescription="@null" />

    </RelativeLayout>
    <!--头像-->
    <!--用户名-->
    <!--时间 -->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp36">

        <!--头像-->

        <ImageView
            android:id="@+id/img_sex_color"
            android:layout_width="@dimen/dp25"
            android:layout_height="@dimen/dp25"
            android:layout_centerVertical="true"
            android:layout_marginLeft="@dimen/dp5"
            android:layout_marginStart="@dimen/dp5"
            android:background="@drawable/shape_image_background"
            android:contentDescription="@null" />

        <com.motoband.widget.RoundImageView
            android:id="@+id/img_story_head_portrait"
            android:layout_width="@dimen/dp23"
            android:layout_height="@dimen/dp23"
            android:layout_centerVertical="true"
            android:layout_marginLeft="@dimen/dp6"
            android:layout_marginStart="@dimen/dp6"
            android:background="@mipmap/me_headportrait_2"
            android:scaleType="centerCrop"
            app:maskType="CIRCLE" />

        <TextView
            android:id="@+id/txt_story_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/dp5"
            android:layout_marginStart="@dimen/dp5"
            android:layout_marginTop="@dimen/dp5"
            android:layout_toEndOf="@+id/img_sex_color"
            android:layout_toRightOf="@+id/img_sex_color"
            android:singleLine="true"
            android:textColor="@color/MD9D9D9"
            android:textSize="@dimen/sp12" />


        <TextView
            android:id="@+id/txt_story_time"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/txt_story_name"
            android:layout_marginLeft="@dimen/dp5"
            android:layout_marginStart="@dimen/dp5"
            android:layout_toEndOf="@+id/img_sex_color"
            android:layout_toRightOf="@+id/img_sex_color"
            android:singleLine="true"
            android:textColor="@color/M969696"
            android:textSize="@dimen/sp8" />

    </RelativeLayout>


    <TextView
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:layout_gravity="center_vertical"
        android:background="@color/M3C3C3C" />

    <!--点赞-->
    <!--评论-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp27"
        android:orientation="horizontal">

        <RelativeLayout
            android:id="@+id/layout_story_like"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1">

            <TextView
                android:id="@+id/txt_story_like"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginLeft="@dimen/dp50"
                android:layout_marginStart="@dimen/dp50"
                android:gravity="center"
                android:text="@string/zero"
                android:textColor="@color/MC8C8C8"
                android:textSize="@dimen/sp11"
                 />

            <ImageView
                android:id="@+id/img_story_like"
                android:layout_width="@dimen/dp18"
                android:layout_height="@dimen/dp18"
                android:layout_centerVertical="true"
                android:layout_marginLeft="@dimen/dp26"
                android:layout_marginStart="@dimen/dp26"
                android:contentDescription="@null" />
            <!--android:background="@drawable/select_dynamic_like"-->
        </RelativeLayout>


        <TextView
            android:layout_width="0.5dp"
            android:layout_height="@dimen/dp20"
            android:layout_gravity="center_vertical"
            android:background="@color/M3C3C3C" />

        <RelativeLayout
            android:id="@+id/layout_story_comment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1">

            <ImageView
                android:id="@+id/img_story_comment"
                android:layout_width="@dimen/dp18"
                android:layout_height="@dimen/dp18"
                android:layout_centerVertical="true"
                android:layout_marginLeft="@dimen/dp26"
                android:layout_marginStart="@dimen/dp26"
                android:background="@mipmap/select_dynamic_comment"
                android:contentDescription="@null" />


            <TextView
                android:id="@+id/txt_story_comment"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginLeft="@dimen/dp50"
                android:layout_marginStart="@dimen/dp50"
                android:gravity="center"
                android:text="@string/zero"
                android:textColor="@color/MC8C8C8"
                android:textSize="@dimen/sp11"
                 />

        </RelativeLayout>

    </LinearLayout>


</LinearLayout>

注:实际开发中根据自身的项目需求去调整

适配还是在Adapter中去写:

 //宽度高度适配   
 //外层的整体item
            WindowManager wm = NewsActivity.this.getWindowManager();
            int width = wm.getDefaultDisplay().getWidth();
            AbsListView.LayoutParams layoutParams = new AbsListView.LayoutParams((width - PixelOrdpManager.dip2px(getBaseContext(), 6)) / 2,
                    (width - PixelOrdpManager.dip2px(getBaseContext(), 6)) / 2 + PixelOrdpManager.dip2px(getBaseContext(), 63));
            convertView.setLayoutParams(layoutParams);

//展示中的正方形照片
            LinearLayout.LayoutParams layoutParamsStory = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            layoutParamsStory.width = (width - PixelOrdpManager.dip2px(getBaseContext(), 6)) / 2;
            layoutParamsStory.height = (width - PixelOrdpManager.dip2px(getBaseContext(), 6)) / 2;
            storyHodelView.layout_story_pic.setLayoutParams(layoutParamsStory);

这里用到了dp与px的转换: xml中一般是dp,代码中是px.
(这里参考 Utils(上)一些常用的工具类)

同理还是写在 return convertView; 之前

GridView的适配就说到这里了,希望有什么问题大家可以共同讨论,相互学习,相互进步。

作者:madreain 发表于2016/8/14 16:13:23 原文链接
阅读:37 评论:0 查看评论

Android 高仿微信支付键盘

$
0
0

现在很多app的支付、输入密码功能,都已经开始使用自定义数字键盘,不仅更加方便、其效果着实精致。
下面带着大家学习下,如何高仿微信的数字键盘,可以拿来直接用在自身的项目中。
先看下效果图:
这里写图片描述

1. 自定义布局

<?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="wrap_content">

    <!-- 输入键盘 -->
    <GridView
        android:id="@+id/gv_keybord"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#bdbdbd"
        android:horizontalSpacing="1px"
        android:numColumns="3"
        android:verticalSpacing="1px" />

    <View
        android:id="@+id/line"
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:layout_above="@id/gv_keybord"
        android:background="#bdbdbd" />

    <RelativeLayout
        android:id="@+id/layoutBack"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/line"
        android:background="#f5f5f5"
        android:padding="10dp">

        <ImageView
            android:id="@+id/imgBack"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@mipmap/keyboard_back_img" />

    </RelativeLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:layout_above="@id/layoutBack"
        android:layout_marginTop="1dp"
        android:background="#bdbdbd" />

</RelativeLayout>

键盘的布局,实质就是一个4X3网格布局的GridView。

2.实现数字键盘内容

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.GridView;
import android.widget.RelativeLayout;

import com.lnyp.pswkeyboard.R;
import com.lnyp.pswkeyboard.adapter.KeyBoardAdapter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 虚拟键盘
 */
public class VirtualKeyboardView extends RelativeLayout implements View.OnClickListener {

    Context context;

    private GridView gridView;    

    private RelativeLayout layoutBack;

    private ArrayList<Map<String, String>> valueList;   


    public VirtualKeyboardView(Context context) {
        this(context, null);
    }

    public VirtualKeyboardView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        View view = View.inflate(context, R.layout.layout_virtual_keyboard, null);

        valueList = new ArrayList<>();

        layoutBack = (RelativeLayout) view.findViewById(R.id.layoutBack);
        layoutBack.setOnClickListener(this);

        gridView = (GridView) view.findViewById(R.id.gv_keybord);

        setView();

        addView(view); 
    }

    public RelativeLayout getLayoutBack() {
        return layoutBack;
    }

    public ArrayList<Map<String, String>> getValueList() {
        return valueList;
    }

    public GridView getGridView() {
        return gridView;
    }

    private void setView() {

        /* 初始化按钮上应该显示的数字 */
        for (int i = 1; i < 13; i++) {
            Map<String, String> map = new HashMap<String, String>();
            if (i < 10) {
                map.put("name", String.valueOf(i));
            } else if (i == 10) {
                map.put("name", ".");
            } else if (i == 11) {
                map.put("name", String.valueOf(0));
            } else if (i == 12) {
                map.put("name", "");
            }
            valueList.add(map);
        }

        KeyBoardAdapter keyBoardAdapter = new KeyBoardAdapter(context, valueList);
        gridView.setAdapter(keyBoardAdapter);
    }

    @Override
    public void onClick(View v) {

    }
}

看下适配器如何处理:KeyBoardAdapter .java

import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.lnyp.pswkeyboard.R;

import java.util.ArrayList;
import java.util.Map;

/**
 * 九宫格键盘适配器
 */
public class KeyBoardAdapter extends BaseAdapter {


    private Context mContext;
    private ArrayList<Map<String, String>> valueList;

    public KeyBoardAdapter(Context mContext, ArrayList<Map<String, String>> valueList) {
        this.mContext = mContext;
        this.valueList = valueList;
    }

    @Override
    public int getCount() {
        return valueList.size();
    }

    @Override
    public Object getItem(int position) {
        return valueList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        if (convertView == null) {
            convertView = View.inflate(mContext, R.layout.grid_item_virtual_keyboard, null);
            viewHolder = new ViewHolder();
            viewHolder.btnKey = (TextView) convertView.findViewById(R.id.btn_keys);
            viewHolder.imgDelete = (RelativeLayout) convertView.findViewById(R.id.imgDelete);

            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        if (position == 9) {
            viewHolder.imgDelete.setVisibility(View.INVISIBLE);
            viewHolder.btnKey.setVisibility(View.VISIBLE);
            viewHolder.btnKey.setText(valueList.get(position).get("name"));
            viewHolder.btnKey.setBackgroundColor(Color.parseColor("#e0e0e0"));
        } else if (position == 11) {
            viewHolder.btnKey.setBackgroundResource(R.mipmap.keyboard_delete_img);
            viewHolder.imgDelete.setVisibility(View.VISIBLE);
            viewHolder.btnKey.setVisibility(View.INVISIBLE);

        } else {
            viewHolder.imgDelete.setVisibility(View.INVISIBLE);
            viewHolder.btnKey.setVisibility(View.VISIBLE);

            viewHolder.btnKey.setText(valueList.get(position).get("name"));
        }

        return convertView;
    }

    /**
     * 存放控件
     */
    public final class ViewHolder {
        public TextView btnKey;
        public RelativeLayout imgDelete;
    }
}

在看Adapter之前,我们先看下grid_item_virtual_keyboard是如何实现的:

<?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"
    android:background="#e0e0e0">

    <TextView
        android:id="@+id/btn_keys"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_centerInParent="true"
        android:background="@drawable/selector_gird_item"
        android:gravity="center"
        android:includeFontPadding="false"
        android:textColor="#333333"
        android:textSize="26sp" />

    <RelativeLayout
        android:id="@+id/imgDelete"
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:layout_centerInParent="true">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@mipmap/keyboard_delete_img" />

    </RelativeLayout>


</RelativeLayout>

可以看到,我们在item布局文件中,指定了两个view,一个是普通显示数字的TextView, 一个是显示最后删除键的RelativeLayout。
然后,在KeyBoardAdapter 的getView方法中,我们根据position位置,对布局进行不同的处理。当position为9,也就是倒数第三个按键,它的按钮颜色要单独设置。 当position为12也就是最后一个按钮时,需要控制删除按钮显示,数字按钮隐藏。 其余情况则是删除按钮隐藏,数字按钮显示。

3.使用并实现键盘事件逻辑

布局中,可以直接使用自己定义的数字键盘:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#efefef"
    tools:context="com.lnyp.pswkeyboard.NormalKeyBoardActivity">


    <EditText
        android:id="@+id/textAmount"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="#FFFFFF"
        android:inputType="numberDecimal"
        android:padding="14dp"
        android:textColor="#333333"
        android:textSize="16sp" />

    <com.lnyp.pswkeyboard.widget.VirtualKeyboardView
        android:id="@+id/virtualKeyboardView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom" />

</RelativeLayout>

我们在Activity中,操作数字键盘:

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.GridView;

import com.lnyp.pswkeyboard.widget.VirtualKeyboardView;

import java.util.ArrayList;
import java.util.Map;

public class NormalKeyBoardActivity extends AppCompatActivity {

    private VirtualKeyboardView virtualKeyboardView;

    private GridView gridView;

    private ArrayList<Map<String, String>> valueList;

    private EditText textAmount;

    private Animation enterAnim;

    private Animation exitAnim;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_normal_key_board);

        valueList = virtualKeyboardView.getValueList();

        initAnim();

        initView();
    }

    private void initAnim() {

        enterAnim = AnimationUtils.loadAnimation(this, R.anim.push_bottom_in);
        exitAnim = AnimationUtils.loadAnimation(this, R.anim.push_bottom_out);
    }

    private void initView() {

        virtualKeyboardView = (VirtualKeyboardView) findViewById(R.id.virtualKeyboardView);
        textAmount = (EditText) findViewById(R.id.textAmount);
        virtualKeyboardView.getLayoutBack().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                virtualKeyboardView.startAnimation(exitAnim);
                virtualKeyboardView.setVisibility(View.GONE);
            }
        });

        gridView = virtualKeyboardView.getGridView();
        gridView.setOnItemClickListener(onItemClickListener);

        textAmount.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                virtualKeyboardView.setFocusable(true);
                virtualKeyboardView.setFocusableInTouchMode(true);

                virtualKeyboardView.startAnimation(enterAnim);
                virtualKeyboardView.setVisibility(View.VISIBLE);
            }
        });

    }

    private AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {

            if (position < 11 && position != 9) {    //点击0~9按钮

                String amount = textAmount.getText().toString().trim();
                amount = amount + valueList.get(position).get("name");

                textAmount.setText(amount);

                Editable ea = textAmount.getText();
                textAmount.setSelection(ea.length());
            } else {

                if (position == 9) {      //点击退格键
                    String amount = textAmount.getText().toString().trim();
                    if (!amount.contains(".")) {
                        amount = amount + valueList.get(position).get("name");
                        textAmount.setText(amount);

                        Editable ea = textAmount.getText();
                        textAmount.setSelection(ea.length());
                    }
                }

                if (position == 11) {      //点击退格键
                    String amount = textAmount.getText().toString().trim();
                    if (amount.length() > 0) {
                        amount = amount.substring(0, amount.length() - 1);
                        textAmount.setText(amount);

                        Editable ea = textAmount.getText();
                        textAmount.setSelection(ea.length());
                    }
                }
            }
        }
    };
}

好了,通过以上步骤,我们便可以实现微信支付键盘啦,是不是很easy呢?!

如有疑问或建议,欢迎进QQ群来讨论交流:487786925( Android研发村 )

源码地址:https://github.com/zuiwuyuan/WeChatPswKeyboard

作者:zuiwuyuan 发表于2016/8/14 16:13:36 原文链接
阅读:81 评论:0 查看评论

[Unity热更新]tolua# & LuaFramework(十四):更新下载(中)

$
0
0

承接上一篇:http://blog.csdn.net/lyh916/article/details/51146693

上一篇基本实现了更新下载,很多时候,我们还需要将更新文件数,下载速度等信息显示在界面上,所以这篇就说下怎么做。


效果图:



Ps:上面的下载速度为Infinity(无穷大),具体原因不明,估计是我使用的是编辑器链接本地服务器,而且更新的文件很小,所以就快得飞起吧!


分析:

这里我把它分为两部分:

a.信息的获取:其实上面显示的信息,都可以通过GameManager.cs获取到。

b.信息的显示:在GameManager.cs中,我们可以发现,当获取到消息后,就会通过facade.SendMessageCommand把消息进行发送,然后被AppView.cs进行捕获处理。

因此目标就很明确了:修改GameManager和AppView。


1.首先,看下facade.SendMessageCommand,它可以发送各种的消息,消息的定义在NotiConst.cs。这里我们把消息的种类扩充一下。

using UnityEngine;
using System.Collections;

public class NotiConst
{
    /// <summary>
    /// Controller层消息通知
    /// </summary>
    public const string START_UP = "StartUp";                       //启动框架
    public const string DISPATCH_MESSAGE = "DispatchMessage";       //派发信息

    /// <summary>
    /// View层消息通知
    /// </summary>
    
    //解包要展示的相关参数
	public const string EXTRACT_FILE_NAME = "EXTRACT_FILE_NAME";           //解包文件名
	public const string EXTRACT_FINISH_ONE = "EXTRACT_FINISH_ONE";         //解包完一个文件   
	public const string EXTRACT_ALL_COUNT = "EXTRACT_ALL_COUNT";           //解包总文件数
	
	//更新要展示的相关参数
    public const string UPDATE_SPEED = "UPDATE_SPEED";                     //下载速度
	public const string UPDATE_FILE_NAME = "UPDATE_FILE_NAME";             //下载文件名
	public const string UPDATE_FINISH_ONE = "UPDATE_FINISH_ONE";           //下载完一个文件
	public const string UPDATE_ALL_COUNT = "UPDATE_ALL_COUNT";             //下载总文件数

    //线程相关
    public const string THREAD_EXTRACT = "THREAD_EXTRACT";                   //线程解包
    public const string THREAD_DOWNLOAD = "THREAD_DOWNLOAD";                 //线程下载
}


2.然后就是准备好解包和更新的参数,并把这些参数发送出去。

修改GameManager.cs

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using LuaInterface;
using System.Reflection;
using System.IO;

namespace LuaFramework {
    public class GameManager : Manager {
        protected static bool initialize = false;
        private List<string> downloadFiles = new List<string>();

        /// <summary>
        /// 初始化游戏管理器
        /// </summary>
        void Awake() {
            Init();
        }

        /// <summary>
        /// 初始化
        /// </summary>
        void Init() {
            if (AppConst.ExampleMode) {
                InitGui();
            }
            DontDestroyOnLoad(gameObject);  //防止销毁自己

            CheckExtractResource(); //释放资源
            Screen.sleepTimeout = SleepTimeout.NeverSleep;
            Application.targetFrameRate = AppConst.GameFrameRate;
        }

        /// <summary>
        /// 初始化GUI
        /// </summary>
        public void InitGui() {
            string name = "UI Root";
            GameObject gui = GameObject.Find(name);
            if (gui != null) return;

            GameObject prefab = Util.LoadPrefab(name);
            gui = Instantiate(prefab) as GameObject;
            gui.name = name;
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void CheckExtractResource() {
            hadExtractResource = Directory.Exists(Util.DataPath) &&
              Directory.Exists(Util.DataPath + "lua/") && File.Exists(Util.DataPath + "files.txt");
            if (hadExtractResource || AppConst.DebugMode)
            {
                ResManager.initialize(OnResourceInited);
                //StartCoroutine(OnUpdateResource());//在第二次解包后就更新
                return;   //文件已经解压过了,自己可添加检查文件列表逻辑
            }
            StartCoroutine(OnExtractResource());    //启动释放协成 
        }

        bool hadExtractResource;
        bool firstExtractResource = true;

        IEnumerator OnExtractResource() {
            string dataPath = Util.DataPath;  //数据目录
            string resPath = Util.AppContentPath(); //游戏包资源目录

            string infile = resPath + "files.txt";
            string outfile = dataPath + "files.txt";
            string message = "";

            if (firstExtractResource)
            {
                //创建Util.DataPath目录
                if (Directory.Exists(dataPath)) Directory.Delete(dataPath, true);
                Directory.CreateDirectory(dataPath);

                if (File.Exists(outfile)) File.Delete(outfile);

                //解包files.txt
                message = "正在解包文件:>files.txt";
                Debug.Log("正在解包文件:>files.txt");
                //facade.SendMessageCommand(NotiConst.EXTRACT_FILE_NAME, message);

                if (Application.platform == RuntimePlatform.Android)
                {
                    WWW www = new WWW(infile);
                    yield return www;

                    if (www.isDone)
                    {
                        File.WriteAllBytes(outfile, www.bytes);
                    }
                    yield return 0;
                }
                else File.Copy(infile, outfile, true);
                //yield return new WaitForEndOfFrame();
            }

            List<string> willExtractFileName = new List<string>();

            //释放文件到数据目录,过滤要解包的文件
            string[] files = File.ReadAllLines(outfile);
            foreach (var file in files) 
            {
                string[] fs = file.Split('|');
                infile = resPath + fs[0];  
                outfile = dataPath + fs[0];

                //start是主界面的包,需要自行修改
                bool a = fs[0].StartsWith("lua/") || fs[0].StartsWith("StreamingAssets") ||fs[0].StartsWith("start");
                if (firstExtractResource && !a) continue;
                if (!firstExtractResource && a) continue;

                //message = "正在解包文件:>" + fs[0];
                //Debug.Log("正在解包文件:>" + infile);
                //facade.SendMessageCommand(NotiConst.EXTRACT_FILE_NAME, fs[0]);

                string dir = Path.GetDirectoryName(outfile);
                if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

                willExtractFileName.Add(fs[0]);
            }

            //第二次解包才显示信息
            if (!firstExtractResource && (willExtractFileName.Count > 0)) 
                facade.SendMessageCommand(NotiConst.EXTRACT_ALL_COUNT, willExtractFileName.Count);

            for (int i = 0; i < willExtractFileName.Count; i++)
			{
                Debug.Log("正在解包:" + willExtractFileName[i]);
                string from = resPath + willExtractFileName[i];
                string to = dataPath + willExtractFileName[i];

                if (Application.platform == RuntimePlatform.Android)
                {
                    if (!firstExtractResource) facade.SendMessageCommand(NotiConst.EXTRACT_FILE_NAME, willExtractFileName[i]);

                    WWW www = new WWW(from);
                    yield return www;

                    if (www.isDone)
                    {
                        File.WriteAllBytes(to, www.bytes);
                        if (!firstExtractResource) facade.SendMessageCommand(NotiConst.EXTRACT_FINISH_ONE, 0);
                    }
                    yield return 0;
                }
                else
                {
                    if (File.Exists(to))
                    {
                        File.Delete(to);
                    }
                    if (!firstExtractResource) facade.SendMessageCommand(NotiConst.EXTRACT_FILE_NAME, willExtractFileName[i]);
                    File.Copy(from, to, true);
                    if (!firstExtractResource) facade.SendMessageCommand(NotiConst.EXTRACT_FINISH_ONE, 0);
                }
                yield return new WaitForEndOfFrame();
			}
;               
            
            if (firstExtractResource)
            {
                ResManager.initialize(OnResourceInited);
            }
            else
            {
                message = "解包完成!!!";
                //facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);

                //释放完成,开始启动更新资源
                StartCoroutine(OnUpdateResource());
            }

            yield return new WaitForSeconds(0.1f);
            message = string.Empty;
        }

        /// <summary>
        /// 启动更新下载,这里只是个思路演示,此处可启动线程下载更新
        /// </summary>
        IEnumerator OnUpdateResource() {
            downloadFiles.Clear();

            if (!AppConst.UpdateMode) {
                //ResManager.initialize(OnResourceInited);
                yield break;
            }
            string dataPath = Util.DataPath;  //数据目录
            string url = AppConst.WebUrl;
            string random = DateTime.Now.ToString("yyyymmddhhmmss");
            string listUrl = url + "files.txt?v=" + random;
            Debug.LogWarning("LoadUpdate---->>>" + listUrl);

            WWW www = new WWW(listUrl); yield return www;
            if (www.error != null) {
                OnUpdateFailed(string.Empty);
                yield break;
            }
            if (!Directory.Exists(dataPath)) {
                Directory.CreateDirectory(dataPath);
            }
            File.WriteAllBytes(dataPath + "files.txt", www.bytes);

            string filesText = www.text;
            string[] files = filesText.Split('\n');

            List<string> willDownLoadUrl = new List<string>();//from
            List<string> willDownLoadFileName = new List<string>();
            List<string> willDownLoadDestination = new List<string>();//to

            string message = string.Empty;
            for (int i = 0; i < files.Length; i++) 
            {
                if (string.IsNullOrEmpty(files[i])) continue;
                string[] keyValue = files[i].Split('|');
                string f = keyValue[0];
                string localfile = (dataPath + f).Trim();
                string path = Path.GetDirectoryName(localfile);
                if (!Directory.Exists(path)) {
                    Directory.CreateDirectory(path);
                }
                string fileUrl = url + keyValue[0] + "?v=" + random;
                bool canUpdate = !File.Exists(localfile);
                if (!canUpdate) {
                    string remoteMd5 = keyValue[1].Trim();
                    string localMd5 = Util.md5file(localfile);
                    canUpdate = !remoteMd5.Equals(localMd5);
                    if (canUpdate) File.Delete(localfile);
                }
                if (canUpdate) {   //本地缺少文件
                    //Debug.Log(fileUrl);
                    //message = "downloading>>" + fileUrl;
                    //facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
                    /*
                    www = new WWW(fileUrl); yield return www;
                    if (www.error != null) {
                        OnUpdateFailed(path);   //
                        yield break;
                    }
                    File.WriteAllBytes(localfile, www.bytes);
                     * */
                    willDownLoadUrl.Add(fileUrl);//下载地址
                    willDownLoadFileName.Add(keyValue[0]);
                    willDownLoadDestination.Add(localfile);//目标文件路径
                }
            }

            if (willDownLoadUrl.Count > 0) facade.SendMessageCommand(NotiConst.UPDATE_ALL_COUNT, willDownLoadUrl.Count);
            for (int i = 0; i < willDownLoadUrl.Count; i++)
            {
                Debug.Log("要下载的文件:" + willDownLoadUrl[i]);
                //这里都是资源文件,用线程下载
                facade.SendMessageCommand(NotiConst.UPDATE_FILE_NAME, willDownLoadFileName[i]);
                BeginDownload(willDownLoadUrl[i], willDownLoadDestination[i]);
                //while (!(IsDownOK(willDownLoadDestination[i]))) { yield return new WaitForEndOfFrame(); }
            }
            yield return new WaitForEndOfFrame();
            message = "更新完成!!";
            //facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);

            //ResManager.initialize(OnResourceInited);
        }

        /// <summary>
        /// 是否下载完成
        /// </summary>
        bool IsDownOK(string file) {
            return downloadFiles.Contains(file);
        }

        /// <summary>
        /// 线程下载
        /// </summary>
        void BeginDownload(string url, string file) {     //线程下载
            object[] param = new object[2] {url, file};

            ThreadEvent ev = new ThreadEvent();
            ev.Key = NotiConst.THREAD_DOWNLOAD;
            ev.evParams.AddRange(param);
            ThreadManager.AddEvent(ev, OnThreadCompleted);   //线程下载
        }

        /// <summary>
        /// 线程完成
        /// </summary>
        /// <param name="data"></param>
        void OnThreadCompleted(NotiData data) {
            switch (data.evName) {
                case NotiConst.THREAD_EXTRACT:  //解压一个完成
                    //
                break;
                case NotiConst.THREAD_DOWNLOAD: //下载一个完成
                    downloadFiles.Add(data.evParam.ToString());
                break;
            }
        }

        void OnUpdateFailed(string file) {
            string message = "更新失败!>" + file;
            Debug.Log(message);
            //facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
        }

        /// <summary>
        /// 资源初始化结束
        /// </summary>
        public void OnResourceInited() {
            LuaManager.InitStart();
            LuaManager.DoFile("Logic/Game");            //加载游戏
            LuaManager.DoFile("Logic/Network");         //加载网络
            NetManager.OnInit();                        //初始化网络

            Util.CallMethod("Game", "OnInitOK");          //初始化完成
            initialize = true;                          //初始化完 

            //类对象池测试
            var classObjPool = ObjPoolManager.CreatePool<TestObjectClass>(OnPoolGetElement, OnPoolPushElement);
            //方法1
            //objPool.Release(new TestObjectClass("abcd", 100, 200f));
            //var testObj1 = objPool.Get();

            //方法2
            ObjPoolManager.Release<TestObjectClass>(new TestObjectClass("abcd", 100, 200f));
            var testObj1 = ObjPoolManager.Get<TestObjectClass>();

            Debugger.Log("TestObjectClass--->>>" + testObj1.ToString());

            //游戏对象池测试
            var prefab = Resources.Load("TestGameObjectPrefab", typeof(GameObject)) as GameObject;
            var gameObjPool = ObjPoolManager.CreatePool("TestGameObject", 5, 10, prefab);

            var gameObj = Instantiate(prefab) as GameObject;
            gameObj.name = "TestGameObject_01";
            gameObj.transform.localScale = Vector3.one;
            gameObj.transform.localPosition = Vector3.zero;

            ObjPoolManager.Release("TestGameObject", gameObj);
            var backObj = ObjPoolManager.Get("TestGameObject");
            backObj.transform.SetParent(null);

            Debug.Log("TestGameObject--->>>" + backObj);

            if (hadExtractResource)
            {
                StartCoroutine(OnUpdateResource());
            }
            else
            {
                firstExtractResource = false;//进行二次解包
                hadExtractResource = true;
                StartCoroutine(OnExtractResource());
            }
        }

        /// <summary>
        /// 当从池子里面获取时
        /// </summary>
        /// <param name="obj"></param>
        void OnPoolGetElement(TestObjectClass obj) {
            Debug.Log("OnPoolGetElement--->>>" + obj);
        }

        /// <summary>
        /// 当放回池子里面时
        /// </summary>
        /// <param name="obj"></param>
        void OnPoolPushElement(TestObjectClass obj) {
            Debug.Log("OnPoolPushElement--->>>" + obj);
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        void OnDestroy() {
            if (NetManager != null) {
                NetManager.Unload();
            }
            if (LuaManager != null) {
                LuaManager.Close();
            }
            Debug.Log("~GameManager was destroyed");
        }
    }
}


3.接着就是把信息显示出来了。

修改AppView.cs

using UnityEngine;
using LuaFramework;
using System.Collections.Generic;

public class AppView : View {
    private string message;

    ///<summary>
    /// 监听的消息
    ///</summary>
    List<string> MessageList {
        get {
            return new List<string>()
            { 
                NotiConst.EXTRACT_FILE_NAME,
                NotiConst.EXTRACT_FINISH_ONE,
                NotiConst.EXTRACT_ALL_COUNT,

                NotiConst.UPDATE_SPEED,
                NotiConst.UPDATE_FILE_NAME,
                NotiConst.UPDATE_FINISH_ONE,
                NotiConst.UPDATE_ALL_COUNT,
            };
        }
    }

    void Awake() {
        RemoveMessage(this, MessageList);
        RegisterMessage(this, MessageList);
    }

    /// <summary>
    /// 处理View消息
    /// </summary>
    /// <param name="message"></param>
    public override void OnMessage(IMessage message) {
        string name = message.Name;
        object body = message.Body;

        this.message = message.Name;
        switch (name) {
            case NotiConst.EXTRACT_FILE_NAME:
                extractFileName = body.ToString();
            break;

            case NotiConst.EXTRACT_FINISH_ONE:
                extractNowCount++;
            break;

            case NotiConst.EXTRACT_ALL_COUNT:
                extractAllCount = (int)body;
            break;

            case NotiConst.UPDATE_SPEED:
                updateSpeed = body.ToString();
            break;

            case NotiConst.UPDATE_FILE_NAME:
                updateFileName = body.ToString();
            break;

            case NotiConst.UPDATE_FINISH_ONE:
                updateNowCount++;
            break;

            case NotiConst.UPDATE_ALL_COUNT:
                updateAllCount = (int)body;
            break;
        }
    }

    string extractFileName;
    int extractNowCount = 0;
    int extractAllCount = 0;

    string updateFileName;
    int updateNowCount = 0;
    int updateAllCount = 0;
    string updateSpeed;

    void OnGUI()
    {
        if (string.IsNullOrEmpty(message)) return;
        if (message.StartsWith("EXTRACT_"))
        {
            GUILayout.Label("正在解包的文件:" + extractFileName);
            GUILayout.Label("当前解包数:" + extractNowCount);
            GUILayout.Label("总的解包数:" + extractAllCount);
        }
        else if (message.StartsWith("UPDATE_"))
        {
            GUILayout.Label("正在下载的文件:" + updateFileName);
            GUILayout.Label("当前下载数:" + updateNowCount);
            GUILayout.Label("总的下载数:" + updateAllCount);
            GUILayout.Label("下载速度:" + updateSpeed);
        }
        
    }

}


4.最后,因为消息类型变了,所以还要修改ThreadManager.cs

using System.Collections;
using System.Threading;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Net;
using System;
using Debug = UnityEngine.Debug;

public class ThreadEvent {
    public string Key;
    public List<object> evParams = new List<object>();
}

public class NotiData {
    public string evName;
    public object evParam;

    public NotiData(string name, object param) {
        this.evName = name;
        this.evParam = param;
    }
}

namespace LuaFramework {
    /// <summary>
    /// 当前线程管理器,同时只能做一个任务
    /// </summary>
    public class ThreadManager : Manager {
        private Thread thread;
        private Action<NotiData> func;
        private Stopwatch sw = new Stopwatch();
        private string currDownFile = string.Empty;

        static readonly object m_lockObj = new object();
        static Queue<ThreadEvent> events = new Queue<ThreadEvent>();

        delegate void ThreadSyncEvent(NotiData data);
        private ThreadSyncEvent m_SyncEvent;

        void Awake() {
            m_SyncEvent = OnSyncEvent;
            thread = new Thread(OnUpdate);
        }

        // Use this for initialization
        void Start() {
            thread.Start();
        }

        /// <summary>
        /// 添加到事件队列
        /// </summary>
        public void AddEvent(ThreadEvent ev, Action<NotiData> func) {
            lock (m_lockObj) {
                this.func = func;
                events.Enqueue(ev);
            }
        }

        /// <summary>
        /// 通知事件
        /// </summary>
        /// <param name="state"></param>
        private void OnSyncEvent(NotiData data) {
            if (this.func != null) func(data);  //回调逻辑层
            facade.SendMessageCommand(data.evName, data.evParam); //通知View层
        }

        // Update is called once per frame
        void OnUpdate() {
            while (true) {
                lock (m_lockObj) {
                    if (events.Count > 0) {
                        ThreadEvent e = events.Dequeue();
                        try {
                            switch (e.Key) {
                                case NotiConst.THREAD_EXTRACT: {     //解压文件
                                    OnExtractFile(e.evParams);
                                }
                                break;
                                case NotiConst.THREAD_DOWNLOAD: {    //下载文件
                                    OnDownloadFile(e.evParams);
                                }
                                break;
                            }
                        } catch (System.Exception ex) {
                            UnityEngine.Debug.LogError(ex.Message);
                        }
                    }
                }
                Thread.Sleep(1);
            }
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        void OnDownloadFile(List<object> evParams) {
            string url = evParams[0].ToString();    
            currDownFile = evParams[1].ToString();

            using (WebClient client = new WebClient()) {
                sw.Start();
                client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
                client.DownloadFileAsync(new System.Uri(url), currDownFile);
            }
        }

        private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e) {
            //UnityEngine.Debug.Log(e.ProgressPercentage);
            /*
            UnityEngine.Debug.Log(string.Format("{0} MB's / {1} MB's",
                (e.BytesReceived / 1024d / 1024d).ToString("0.00"),
                (e.TotalBytesToReceive / 1024d / 1024d).ToString("0.00")));
            */
            //float value = (float)e.ProgressPercentage / 100f;

            string value = string.Format("{0} kb/s", (e.BytesReceived / 1024d / sw.Elapsed.TotalSeconds).ToString("0.00"));
            NotiData data = new NotiData(NotiConst.UPDATE_SPEED, value);
            if (m_SyncEvent != null) m_SyncEvent(data);

            if (e.ProgressPercentage == 100 && e.BytesReceived == e.TotalBytesToReceive)
            {
                sw.Reset();

                data = new NotiData(NotiConst.UPDATE_FINISH_ONE, 0);
                if (m_SyncEvent != null) m_SyncEvent(data);
            }
        }

        /// <summary>
        /// 调用方法
        /// </summary>
        void OnExtractFile(List<object> evParams) {
            Debugger.LogWarning("Thread evParams: >>" + evParams.Count);

            ///------------------通知更新面板解压完成--------------------
            //NotiData data = new NotiData(NotiConst.UPDATE_DOWNLOAD, null);
            //if (m_SyncEvent != null) m_SyncEvent(data);
        }

        /// <summary>
        /// 应用程序退出
        /// </summary>
        void OnDestroy() {
            thread.Abort();
        }
    }
}


作者:lyh916 发表于2016/8/14 16:29:41 原文链接
阅读:44 评论:0 查看评论

Android UI(ActionBar+Toolbar)详解

$
0
0
目录:
    1.ActionBar与ToolBar概述
    2.ActionBar与ToolBar的应用场景
    3.ActionBar的隐藏
        3.1 通过自定义Theme隐藏
        3.2 通过系统Theme隐藏
        3.3 通过Java代码隐藏
    4.ToolBar的使用
        4.1 自定义Theme中的一些常用属性
        4.2 基本使用
        4.3 一些问题?(标题/子标题/overflow文字的颜色和字体+overflow icon设置和flow menu背景等)    
         
1.ActionBar与ToolBar概述
    ActionBar是在Android 3.0推出的,google为了改善原本混乱的App风格设计,在2014年的google I/O大会上推出了material design
而ToolBar就是在Android 5.0中用来替换ActionBar的解决方案,同时ActionBar和ToolBar在material design 中被统称为了App bar,说了
这么多可能有些小伙伴还是不知道他到底长啥样,都有啥功能。下面来张截图:

2.ActionBar与ToolBar的应用场景
    ActionBar与ToolBar的应用场景主要在不同Activity之间进行前置导航或者是一些必要的功能按钮或者是导航提示,同时也支持Action
    overflow(即下拉导航列表,类似于下拉菜单)
    
3.ActionBar的隐藏
    3.1 通过自定义Theme隐藏
        1)在styles.xml中定义Theme        
   <style name="noActionbarTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- 设置不显示ActionBar -->
        <item name="windowActionBar">false</item>
        <!--设置不显示title-->
        <item name="windowNoTitle">true</item>

    </style>

        2)在Manifest.xml的application或者Activity节点中田间android:theme="@style/noActionbarTheme"
        
    3.2 通过系统Theme隐藏
 android:theme="@style/Theme.AppCompat.NoActionBar"
 android:theme="@style/Theme.AppCompat.Light.NoActionBar"      

     隐藏效果    


    3.3 通过Java代码隐藏
package com.example.androiduiappbar;

import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //实例化ActionBar,注意为了向下兼容我们使用了support.v7.app.ActionBar
        ActionBar actionBar = getSupportActionBar();
        //隐藏ActionBar
        actionBar.hide();
    }
}

4.ToolBar的使用
    4.1 自定义Theme中的一些常用属性
    1)styles.xml中定义myTheme
   <style name="myTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- 设置不显示ActionBar -->
       <!--<item name="windowActionBar">false</item>-->
       <!--设置不显示title-->
        <!--  <item name="windowNoTitle">true</item>-->
        <!--ActionBar颜色-->
        <item name="colorPrimary">@color/colorAccent</item>
        <!--状态栏颜色-->
        <item name="colorPrimaryDark">@color/accent_material_light</item>
        <!--设置window中控件的,比如:checkbox、switch等checked或selected时的颜色-->
        <item name="colorAccent">@color/colorAccent1</item>
        <!--设置window布局背景色-->
        <item name="android:windowBackground">@color/dim_foreground_material_dark</item>
    </style>

    
    2)应用于Aplication
   
<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/myTheme"
        >
       

    3)效果截图:

 

    
    4.2 基本使用
    1)styles.xml自定义Theme隐藏Actionbar
   
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <!--自定义无Actionbar的主题-->
    <style name="noActionbarTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- 设置不显示ActionBar -->
        <item name="windowActionBar">false</item>
        <!--设置不显示title-->
        <item name="windowNoTitle">true</item>
    </style>

</resources>

    2)manifest.xml中activity应用自定义Theme
    
 <activity android:name=".SecondActivity" android:theme="@style/noActionbarTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
</activity>    
       
    2)主布局文件activity_second.xml
   
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.androiduiappbar.SecondActivity">
     <!--
     android:background="":可设置toolbar的背景
     -->   
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolBar"
        android:layout_height="?attr/actionBarSize"
        android:layout_width="match_parent"
        android:background="@color/colorAccent1"
        />
<ImageView
    android:layout_below="@+id/toolBar"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/pinkgirl"/>
</RelativeLayout>
 
    3)toolbar action menu定义toolbar_menu.xml
    
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    tools:context=".SecondActivity">
    <!--
    属性用法:
     android:title="":设置自定义action的标题文字,放入action flow中时只显示文字
     android:orderInCategory="":表示action在app bar上显示的顺序,值越小越靠前
     android:icon="":设置显示的图标
     app:showAsAction="ifRoom":设置显示的位置,ifRoom(有空间显示,没空间显示到overflow)
        never(一直显示到overflow),alaways(一直显示在app bar上),
        withText(设置图标和文字一起显示)
    -->
    <!--查询按钮-->
   <item android:id="@+id/action_serach"
        android:title="@string/action_search"
        android:orderInCategory="1"
       android:icon="@drawable/search_24"
        app:showAsAction="ifRoom"
        />
    <!--分享-->
    <item android:id="@+id/action_share"
        android:title="@string/action_share"
        android:orderInCategory="2"
        app:showAsAction="ifRoom"
        android:icon="@drawable/share_24"
        />
    <!--设置-->
    <item android:id="@+id/action_setting"
        android:title="@string/action_setting"
        android:orderInCategory="3"
        app:showAsAction="never"
        android:icon="@drawable/back"
        />
    <!--更多-->
    <item android:id="@+id/action_more"
        android:title="@string/action_more"
        android:orderInCategory="4"
        app:showAsAction="never"
        android:icon="@drawable/share_24b"
        />
</menu>

    4)主java类SecondActivity.java
package com.example.androiduiappbar;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolBar);
        //设置应用图标
        toolbar.setLogo(R.mipmap.ic_launcher);
        //设置标题,也可在xml中实现toolbar:title="标题"
        toolbar.setTitle("标题");
        //设置子标题
        toolbar.setSubtitle("子标题");
        //设置导航图标
        toolbar.setNavigationIcon(R.drawable.back);
        //设置toolbar
        //注意这句代码必须放在自定义按钮监听和设置导航条Icon之前
        setSupportActionBar(toolbar);
        //自定义去的按钮点击监听
        toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()){
                    case R.id.action_serach:
                        Log.d("ItemId",item.getItemId()+"");
                        Toast.makeText(SecondActivity.this,"你点击了搜索!",Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.action_share:
                        Log.d("ItemId",item.getItemId()+"");
                        Toast.makeText(SecondActivity.this,"你点击了分享!",Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.action_setting:
                        Log.d("ItemId",item.getItemId()+"");
                        Toast.makeText(SecondActivity.this,"你点击了设置!",Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.action_more:
                        Log.d("ItemId",item.getItemId()+"");
                        Toast.makeText(SecondActivity.this,"你点击了更多!",Toast.LENGTH_SHORT).show();
                        break;
                    default:
                        Log.d("ItemId",item.getItemId()+"");
                        break;
                }

                return false;
            }
        });

        //设置导航按钮监听
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(SecondActivity.this,"你点击了导航按钮!",Toast.LENGTH_SHORT).show();
            }
        });
    }
    /*
    * 继承实现onCreateOptionsMenu方法实例化inflater去加载布局
    * 另一种方法是通过toolbar.inflateMenu(R.menu.toolbar_menu);
    * */

    @Override
    public boolean onCreateOptionsMenu(Menu menu){
        MenuInflater inflater =getMenuInflater();
        inflater.inflate(R.menu.toolbar_menu,menu);
        return super.onCreateOptionsMenu(menu);
    }
}


ps:需要用support v7里面的toolbar,否则只有API 21(Android 5.0)以上才能使用
    
    5)效果截图(原谅我花痴的用了一张萌妹纸的照片,贼笑.jpg)
    
    
    4.3 一些问题?(标题/子标题/overflow文字的颜色和字体+overflow icon设置和flow menu背景等)    
    1)标题/子标题的颜色字体设置
    java代码设置:
  
        //子标题颜色和字体设置,因为Theme_Toolbar_Title中已经设置字体颜色所以setTitleTextAppearance可以不用    
        //toolbar.setTitleTextColor(R.color.colorAccent);
        toolbar.setTitleTextAppearance(SecondActivity.this,R.style.Theme_Toolbar_Title);
        //子标题颜色和字体设置,因为Theme_Toolbar_Subtitle中已经设置字体颜色所以setSubtitleTextColor可以不用
        //toolbar.setSubtitleTextColor(getResources().getColor(android.R.color.holo_blue_light));
        toolbar.setSubtitleTextAppearance(SecondActivity.this,R.style.Theme_Toolbar_Subtitle);

        
    styles.xml中定义Theme:
   
       <!--标题文字颜色和字体大小-->
        <style name="Theme_Toolbar_Title" parent="@style/TextAppearance.Widget.AppCompat.Toolbar.Title">
            <item name="android:textSize">20dp</item>
            <item name="android:textColor">@color/colorAccent</item>
        </style>
        <!--子标题文字颜色和字体大小-->
        <style name="Theme_Toolbar_Subtitle" parent="@style/TextAppearance.Widget.AppCompat.Toolbar.Subtitle">
            <item name="android:textSize">18dp</item>
            <item name="android:textColor">@color/colorAccent</item>
        </style>

    2)overflow文字的样式+背景
       
    自定义style:
    
    <!--设置overflow背景和文字样式-->
    <style name="PopupOverflow" parent="@style/ThemeOverlay.AppCompat.Dark">
        <item name="android:textColorPrimary">@color/textcolorPrimary</item>
        <item name="android:textSize">16sp</item>
        <item name="android:background">@color/textColor</item>
    </style>
   
    在主布局Toolbar中添加app:popupTheme="@style/PopupOverflow":
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolBar"
            android:layout_height="?attr/actionBarSize"
            android:layout_width="match_parent"
            android:background="@color/colorAccent1"
            app:popupTheme="@style/PopupOverflow"
            />
      
    3)overflow icon设置
    定义style自定义Overflow图标:
        <style name="customOverflowButtonStyle" parent="android:style/Widget.Holo.Light.ActionButton.Overflow">
            <item name="android:src">@drawable/expand_more_24</item>
        </style>

    在Activity应用的Theme中添加<item name="actionOverflowButtonStyle">@style/customOverflowButtonStyle</item>:

        <!--自定义无Actionbar的主题-->
        <style name="noActionbarTheme" parent="Theme.AppCompat.Light.DarkActionBar">
            <!-- 设置不显示ActionBar -->
            <item name="windowActionBar">false</item>
            <!--设置不显示title-->
            <item name="windowNoTitle">true</item>
            <!--引用自定义图标style-->
            <item name="actionOverflowButtonStyle">@style/customOverflowButtonStyle</item>
        </style>
    
    4)效果截图(可与更改之前的对比)
    
    
    参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1118/2006.html   

作者:qq_28057577 发表于2016/8/14 16:57:22 原文链接
阅读:22 评论:0 查看评论

AndroidStudio几个常见快捷键和实用技巧

$
0
0

本文只是给大家提供一个思路,如果能提升工作效率那最好了。

  • shift + command + a
    查找动作


如上,我们可以快捷的打开java autoimport开关

  • shift + shift
    全局查找


可以方便的查找资源文件、类等

  • control + space
    代码提示


这个估计是IDE的最强feature了吧

  • shift + control + space
    根据类型提示


根据需要的类型提示,这个还是挺实用的

  • command + d
    复制一整行

  • alt + right/left
    单词为单位移动光标


同时按住shift的话就可以在行内灵活选择了

  • alt + up/down
    多行选择 扩大/缩小



  • shift + alt + up/down
    移动当前行或选中的代码


  • tab
    使用tab代替enter可以替换之前的代码




  • 使用模板


as提供了很多模板,更多去设置中live templates中查看吧。


  • 自定义模板


使用时只要使用缩写就能很方便的生成代码了。


  • 给断点设置表达式
    左键设置断点,右键唤出菜单



debug运行后,会在console中打印表达式的值




  • 结构化查找
    按照模板查找代码



  • 结构化检查
    按照指定模板检查代码,可以指定警告级别



  • 结构化替换




关于Java和Android大牛频道

Java和Android大牛频道是一个数万人关注的探讨Java和Android开发的公众号,分享和原创最有价值的干货文章,让你成为这方面的大牛

我们探讨android和Java开发最前沿的技术:android性能优化 ,插件化,动态化,跨平台,动态化,加固和反破解等,也讨论设计模式/软件架构等。由群来自BAT的工程师组成的团队

关注即送红包,回复:“百度” 、“阿里”、“腾讯” 有惊喜!!!关注后可用入微信群。群里都是来自百度阿里腾讯的大牛。

欢迎关注我们,一起讨论技术,扫描和长按下方的二维码可快速关注我们。搜索微信公众号:JANiubility。


公众号:JANiubility

如想加群讨论学习,请关注微信公众号点击右下角的“加群学习”菜单入群。

作者:xhmj12 发表于2016/8/14 17:07:47 原文链接
阅读:18 评论:0 查看评论

Android 仿微信的键盘切换

$
0
0

Android 仿微信的键盘切换

Android 仿微信的键盘切换(录音,表情,文字,其他),IM通讯,类似朋友圈只要涉及到文字等相关的app都会要涉及到键盘的处理,今天就给大家分享一下Android 仿微信的键盘切换。

效果图如下:

初始界面
录音
表情
其他 照片 拍摄 小视频  位置

Android 仿微信的键盘切换,实现了录音、表情、其他和软键盘显示之间的切换,其中解决了很多博客介绍的键盘切换时,软键盘显示切换到表情(其他)时,出现屏幕晃动的情况,以及点击和滑动键盘显示区域外时,软键盘隐藏的功能等,废话不多说直接上代码,以供大家参考:

xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/linearlayout_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/M323535"
    android:fitsSystemWindows="true"
    android:orientation="vertical"
    tools:context=".ui.activity.ChatActivity">

    <RelativeLayout
        android:id="@+id/layout_title"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp44"
        android:background="@color/title_bar"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/img_finish"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="@dimen/dp3"
            android:layout_marginStart="@dimen/dp3"
            android:background="@drawable/select_image_finsh"
            android:contentDescription="@null" />

        <TextView
            android:id="@+id/txt_finish"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:layout_toEndOf="@+id/img_finish"
            android:layout_toRightOf="@+id/img_finish"
            android:gravity="center"
            android:text="@string/img_finish"
            android:textColor="@drawable/text_selector"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/txt_im_object_name"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:clickable="false"
            android:gravity="center"
            android:textColor="@color/MFFCE00"
            android:textSize="@dimen/sp20" />


    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/keyboardListenRelativeLayout"
        android:layout_below="@+id/layout_title">

        <ListView
            android:id="@+id/listView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:divider="@null" />

        <!--<com.motoband.ui.Voice.RecordButton-->
            <!--android:id="@+id/btn_record"-->
            <!--android:layout_width="match_parent"-->
            <!--android:layout_height="wrap_content"-->
            <!--android:layout_alignParentBottom="true"-->
            <!--android:layout_centerHorizontal="true"-->
            <!--/>-->

        <!--//语音-->
        <RelativeLayout
            android:id="@+id/layout_voice"
            android:layout_width="@dimen/dp200"
            android:layout_height="@dimen/dp200"
            android:layout_centerInParent="true"
            android:visibility="gone">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/chat_window_backgroud"
                android:contentDescription="@null" />

            <ImageView
                android:id="@+id/img_im_microphone_state"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="@dimen/dp16"
                android:background="@mipmap/im_microphone"
                android:contentDescription="@null" />

            <ImageView
                android:id="@+id/img_im_microphone_sound_size"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/img_im_microphone_state"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="@dimen/dp11"
                android:background="@mipmap/chat_volume_06"
                android:contentDescription="@null" />


            <TextView
                android:id="@+id/txt_im_microphone_show_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@+id/img_im_microphone_sound_size"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="@dimen/dp26"
                android:contentDescription="@null"
                android:text="@string/slide_to_cancel_sending"
                android:textColor="@color/M3C3C3C"
                android:textSize="@dimen/sp16" />


        </RelativeLayout>

        <!--<TextView-->
            <!--android:id="@+id/txt_start"-->
            <!--android:layout_width="match_parent"-->
            <!--android:layout_height="@dimen/dp44"-->
            <!--android:background="@color/M44C494"-->
            <!--android:text="播放语音"-->
            <!--/>-->

    </RelativeLayout>


    <com.motoband.ui.view.KeyboardListenRelativeLayout
        android:id="@+id/keyboardListenRelativeLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fillViewport="true">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <!--发布评论-->
                <LinearLayout
                    android:id="@+id/layout_release"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/M575A5B"
                    android:orientation="horizontal"
                    tools:ignore="UselessParent">

                    <ImageView
                        android:id="@+id/img_voice"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:layout_marginLeft="@dimen/dp12"
                        android:layout_marginStart="@dimen/dp12"
                        android:contentDescription="@null" />

                    <!--//文字  与录音-->
                    <RelativeLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_weight="1">

                        <com.motoband.ui.view.MyEditText
                            android:id="@+id/txt_im_message"
                            android:layout_width="280dp"
                            android:layout_height="@dimen/dp30"
                            android:layout_gravity="center_vertical"
                            android:layout_marginBottom="@dimen/dp8"
                            android:layout_marginLeft="@dimen/dp15"
                            android:layout_marginStart="@dimen/dp15"
                            android:layout_marginTop="@dimen/dp8"
                            android:background="@drawable/shape_d8d8d8"
                            android:gravity="start|top"
                            android:hint="@string/say_some"

                            android:imeOptions="actionDone"

                            android:inputType="textMultiLine"
                            android:maxLines="6"
                            android:paddingBottom="@dimen/dp7"
                            android:paddingLeft="@dimen/dp7"

                            android:paddingRight="@dimen/dp7"
                            android:paddingTop="@dimen/dp7"
                            android:singleLine="false"
                            android:textColor="@color/M828282"
                            android:textColorHint="@color/M828282"
                            android:textCursorDrawable="@drawable/shape_cursor_color"
                            android:textSize="@dimen/sp12"

                            />

                        <TextView
                            android:id="@+id/txt_record"
                            android:layout_width="match_parent"
                            android:layout_height="@dimen/dp30"
                            android:layout_marginBottom="@dimen/dp8"
                            android:layout_marginLeft="@dimen/dp15"
                            android:layout_marginStart="@dimen/dp15"
                            android:layout_marginTop="@dimen/dp8"
                            android:background="@drawable/shape_im_chat_voice_up"
                            android:gravity="center"
                            android:paddingBottom="@dimen/dp7"
                            android:paddingLeft="@dimen/dp7"
                            android:paddingRight="@dimen/dp7"
                            android:paddingTop="@dimen/dp7"
                            android:text="@string/txt_record"
                            android:visibility="gone" />

                    </RelativeLayout>


                    <ImageView
                        android:id="@+id/img_expression"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:layout_marginLeft="@dimen/dp12"
                        android:layout_marginStart="@dimen/dp12"

                        android:contentDescription="@null" />

                    <ImageView
                        android:id="@+id/img_other"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:layout_marginEnd="@dimen/dp12"
                        android:layout_marginLeft="@dimen/dp12"
                        android:layout_marginRight="@dimen/dp12"
                        android:layout_marginStart="@dimen/dp12"
                        android:contentDescription="@null" />


                </LinearLayout>


                <!--//表情-->
                <RelativeLayout
                    android:id="@+id/layout_expression"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    android:layout_below="@+id/layout_release"
                    android:background="@color/MFFFFFF"
                    android:visibility="gone">

                    <android.support.v4.view.ViewPager
                        android:id="@+id/viewPager"
                        android:layout_width="match_parent"
                        android:layout_height="180dp" />

                    <LinearLayout
                        android:id="@+id/viewGroup"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_alignParentBottom="true"
                        android:layout_marginBottom="@dimen/dp10"
                        android:background="@color/MFFFFFF"
                        android:gravity="center_horizontal"
                        android:orientation="horizontal"
                        android:paddingLeft="@dimen/dp20"
                        android:paddingRight="@dimen/dp20" />

                </RelativeLayout>

                <!--//其他  照片  拍摄   小视频  位置-->
                <RelativeLayout
                    android:id="@+id/layout_other"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    android:layout_below="@+id/layout_release"
                    android:background="@color/M323535"
                    android:visibility="gone">


                    <LinearLayout
                        android:id="@+id/layout_other_item"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="@dimen/dp25"
                        android:orientation="horizontal">

                        <ImageView
                            android:id="@+id/im_chat_poto"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="@dimen/dp16"
                            android:layout_marginStart="@dimen/dp16"
                            android:background="@mipmap/im_chat_poto"
                            android:contentDescription="@null" />

                        <ImageView
                            android:id="@+id/im_chat_camera"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="@dimen/dp16"
                            android:layout_marginStart="@dimen/dp16"
                            android:background="@mipmap/im_chat_camera"
                            android:contentDescription="@null" />

                        <ImageView
                            android:id="@+id/im_chat_video"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="@dimen/dp16"
                            android:layout_marginStart="@dimen/dp16"
                            android:background="@mipmap/im_chat_video"
                            android:contentDescription="@null" />

                        <ImageView
                            android:id="@+id/im_chat_location"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="@dimen/dp16"
                            android:layout_marginStart="@dimen/dp16"
                            android:background="@mipmap/im_chat_poto"
                            android:contentDescription="@null" />


                    </LinearLayout>


                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_below="@+id/layout_other_item"
                        android:layout_marginEnd="@dimen/dp8"
                        android:layout_marginLeft="@dimen/dp8"
                        android:layout_marginRight="@dimen/dp8"
                        android:layout_marginStart="@dimen/dp8"
                        android:layout_marginTop="@dimen/dp7"
                        android:orientation="horizontal">

                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_gravity="center"
                            android:layout_weight="1"
                            android:gravity="center"
                            android:text="@string/newtype_photo"
                            android:textColor="@color/M969999"
                            android:textSize="@dimen/sp11" />


                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_gravity="center"
                            android:layout_weight="1"
                            android:gravity="center"
                            android:text="@string/select_takephoto"
                            android:textColor="@color/M969999"
                            android:textSize="@dimen/sp11" />

                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_gravity="center"
                            android:layout_weight="1"
                            android:gravity="center"
                            android:text="@string/newtype_video"
                            android:textColor="@color/M969999"
                            android:textSize="@dimen/sp11" />

                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_gravity="center"
                            android:layout_weight="1"
                            android:gravity="center"
                            android:text="@string/im_location"
                            android:textColor="@color/M969999"
                            android:textSize="@dimen/sp11" />


                    </LinearLayout>

                    <!--<ImageView-->
                    <!--android:id="@+id/img_bitmap"-->
                    <!--android:layout_width="wrap_content"-->
                    <!--android:layout_height="wrap_content" />-->

                </RelativeLayout>


            </RelativeLayout>


        </ScrollView>


    </com.motoband.ui.view.KeyboardListenRelativeLayout>


    <!--//存放当前位置照片-->
    <ScrollView
        android:id="@+id/scrollView_location_bitmap"
        android:layout_width="match_parent"
        android:layout_height="340dp"
        android:layout_marginTop="@dimen/dp800">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="340dp"
            android:orientation="vertical"
            tools:ignore="ScrollViewSize">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="203dp">

                <com.amap.api.maps.MapView
                    android:id="@+id/map_location"
                    android:layout_width="match_parent"
                    android:layout_height="203dp"
                    android:layout_gravity="center" />

                <ImageView
                    android:id="@+id/img_location_map"
                    android:layout_width="match_parent"
                    android:layout_height="203dp"
                    android:layout_gravity="center"
                    android:contentDescription="@null"
                    android:scaleType="centerCrop" />


            </RelativeLayout>
        </LinearLayout>
    </ScrollView>


</RelativeLayout>

xml布局中用到了自定义KeyboardListenRelativeLayout(判断软键盘显示和隐藏的自定义控件)

package com.motoband.ui.view;
/**
 * Created by admin on 2016/4/22.
 */

import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;

/**
 * @author madreain
 */
public class KeyboardListenRelativeLayout extends RelativeLayout {

    private static final String TAG = KeyboardListenRelativeLayout.class.getSimpleName();

    public static final byte KEYBOARD_STATE_SHOW = -3;
    public static final byte KEYBOARD_STATE_HIDE = -2;
    public static final byte KEYBOARD_STATE_INIT = -1;

    private boolean mHasInit = false;
    private boolean mHasKeyboard = false;
    private int mHeight;

    private IOnKeyboardStateChangedListener onKeyboardStateChangedListener;

    public KeyboardListenRelativeLayout(Context context) {
        super(context);
    }
    public KeyboardListenRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyboardListenRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setOnKeyboardStateChangedListener(IOnKeyboardStateChangedListener onKeyboardStateChangedListener) {
        this.onKeyboardStateChangedListener = onKeyboardStateChangedListener;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if(!mHasInit) {
            mHasInit = true;
            mHeight = b;
            if(onKeyboardStateChangedListener != null) {
                onKeyboardStateChangedListener.onKeyboardStateChanged(KEYBOARD_STATE_INIT);
            }
        } else {
            mHeight = mHeight < b ? b : mHeight;
        }

        if(mHasInit && mHeight > b) {
            mHasKeyboard = true;
            if(onKeyboardStateChangedListener != null) {
                onKeyboardStateChangedListener.onKeyboardStateChanged(KEYBOARD_STATE_SHOW);
            }
        }
        if(mHasInit && mHasKeyboard && mHeight == b) {
            mHasKeyboard = false;
            if(onKeyboardStateChangedListener != null) {
                onKeyboardStateChangedListener.onKeyboardStateChanged(KEYBOARD_STATE_HIDE);
            }
        }
    }

    public interface IOnKeyboardStateChangedListener {
        public void onKeyboardStateChanged(int state);
    }
}

下面直接步入正题来介绍代码中实现的键盘切换:

package com.motoband.ui.activity;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Environment;
import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.maps.AMap;
import com.amap.api.maps.AMapOptions;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.LocationSource;
import com.amap.api.maps.MapView;
import com.amap.api.maps.UiSettings;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.CameraPosition;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.MyLocationStyle;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.geocoder.GeocodeResult;
import com.amap.api.services.geocoder.GeocodeSearch;
import com.amap.api.services.geocoder.RegeocodeQuery;
import com.amap.api.services.geocoder.RegeocodeResult;
import com.motoband.R;
import com.motoband.core.manager.LoginManager;
import com.motoband.core.manager.MBUserManager;
import com.motoband.core.model.MBUserModel;
import com.motoband.core.utils.MBUtil;
import com.motoband.mbcamera.camera.CameraConstants;
import com.motoband.ui.Voice.AudioRecorder;
import com.motoband.ui.Voice.RecordButton;
import com.motoband.ui.manager.PixelOrdpManager;
import com.motoband.ui.manager.SoftKeyboardManager;
import com.motoband.ui.manager.StatusBarManager;
import com.motoband.ui.view.KeyboardListenRelativeLayout;
import com.motoband.ui.view.MyEditText;
import com.motoband.ui.view.MyGridView;
import com.motoband.widget.RoundImageView;
import com.squareup.picasso.Picasso;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class ChatActivity extends AppCompatActivity implements LocationSource,AMapLocationListener,GeocodeSearch.OnGeocodeSearchListener {


    private Intent intent;
    private MBUserModel mbUserModel;
    private MBUserModel meMBUserModel;
    //聊天对象
    private TextView txt_im_object_name;

    private ListView listView;
//    private ChatAdapter chatAdapter;


    //信息内容
    private MyEditText txt_im_message;

    //键盘
    //最外层判断软键盘是否弹出的
    private KeyboardListenRelativeLayout keyboardListenRelativeLayout;

    //语音
    private ImageView img_voice;
    //录音
    private TextView txt_record;
    private RelativeLayout layout_voice;
    private ImageView img_im_microphone_state;
    private ImageView img_im_microphone_sound_size;
    private TextView txt_im_microphone_show_text;

    //表情
    private ImageView img_expression;

    //其他  照片  拍摄   小视频   位置
    private ImageView img_other;
    //其他的显示
    private RelativeLayout layout_other;
    //照片
    private ImageView im_chat_poto;
    // 拍摄
    private ImageView im_chat_camera;
    //照片路径
    private String picStringUrl;
    // 小视频
    private ImageView im_chat_video;
    // 位置
    private ImageView im_chat_location;
    //位置的照片   //地图
    private ScrollView scrollView_location_bitmap;
    private MapView mapView;//骑行地图   TextureMapView
    private AMap aMap;//骑行地图
    private ImageView img_location_map;
    //定位
    private AMapLocationClient mLocationClient;
    private LocationSource.OnLocationChangedListener mListener;
    private LatLonPoint latLonPoint;
    private GeocodeSearch geocodeSearch;
    //获取的当前位置
    private LocationManager locationManager;
    private String locationProvider;
    Location location;
    //经纬度
    private double latitude;
    private double longitude;
    //判断是点击的还是移动地图的   false  移动地图  true  点击
    private boolean isFirst=false;
    private boolean isShowMoveLocationCenter=true;
    // 获取当前地图中心点的坐标
    LatLng mTarget;
    //地图中心点当前的具体位置
//    private LatLng currentlatLng;
    private String currentSpecificPosition;
//    private ImageView img_bitmap;




    //语音
    private boolean isShowVoice = false;
    //表情
    private boolean isShowExpressionViewpager = false;
    //其他
    private boolean isShowOther = false;
    private RelativeLayout layout_expression;
    private ViewPager viewPager;
    private ViewPagerAdapter viewPagerAdapter;
    private List<View> viewsList;
    /**
     * 将小圆点的图片用数组表示
     */
    private ImageView[] imageViews;
    //包裹小圆点的LinearLayout
    private LinearLayout viewPoints;
    private ImageView imageView;
    //第一组表情
    private MyGridView myGridViewExpresionOne;
    ArrayList<Integer> unicodeOne = new ArrayList<Integer>();
    //第二组表情
    private MyGridView myGridViewExpresionTwo;
    ArrayList<Integer> unicodeTwo = new ArrayList<Integer>();
    //第三组表情
    private MyGridView myGridViewExpresionThere;
    ArrayList<Integer> unicodeThere = new ArrayList<Integer>();
    private GridAdapter gridAdapter;

    //当前选择的是录音  表情   其他 并且显示 1  2   3
    private int currentSelect = 0;

//    RecordButton button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);
        //在进入有VideoView界面的Activity时会出现闪黑屏的情况(如论视频是否播放):
        getWindow().setFormat(PixelFormat.TRANSLUCENT);
        //状态栏颜色的设置
        RelativeLayout linearLayout = (RelativeLayout) findViewById(R.id.linearlayout_main);
        StatusBarManager.SetStatusBar(getWindow(), this, getResources(), "#1F2B29", linearLayout);

        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);

        initMap(savedInstanceState);
        initView();
        initData();
        initListener();

//        button = (RecordButton) findViewById(R.id.btn_record);
//        button.setAudioRecord(new AudioRecorder());
//        button.setRecordListener(new RecordButton.RecordListener() {
//            @Override
//            public void recordEnd(String filePath) {
//
//
//            }
//        });
    }


    private void initView() {
//        img_bitmap= (ImageView) findViewById(R.id.img_bitmap);
        listView = (ListView) findViewById(R.id.listView);
        txt_im_object_name = (TextView) findViewById(R.id.txt_im_object_name);
        //说点什么
        txt_im_message = (MyEditText) findViewById(R.id.txt_im_message);
        txt_im_message.setSelection(txt_im_message.getText().length());
        //录音
        txt_record = (TextView) findViewById(R.id.txt_record);
        //录音显示的控件
        initVoice();

//        wakeLock = ((PowerManager) getSystemService(Context.POWER_SERVICE))
//                .newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "demo");

        //键盘
        initKeyboard();

    }

    //录音显示的控件
    private void initVoice() {

        layout_voice = (RelativeLayout) findViewById(R.id.layout_voice);
        img_im_microphone_state = (ImageView) findViewById(R.id.img_im_microphone_state);
        img_im_microphone_sound_size = (ImageView) findViewById(R.id.img_im_microphone_sound_size);
        txt_im_microphone_show_text = (TextView) findViewById(R.id.txt_im_microphone_show_text);

    }

    private void initKeyboard() {

        //输入框栏   用来判断软键盘是否弹出
        keyboardListenRelativeLayout = (KeyboardListenRelativeLayout) findViewById(R.id.keyboardListenRelativeLayout);
        layout_expression = (RelativeLayout) findViewById(R.id.layout_expression);
        viewPager = (ViewPager) findViewById(R.id.viewPager);
        viewPoints = (LinearLayout) findViewById(R.id.viewGroup);
        img_voice = (ImageView) findViewById(R.id.img_voice);
        img_expression = (ImageView) findViewById(R.id.img_expression);
        img_other = (ImageView) findViewById(R.id.img_other);
        layout_other = (RelativeLayout) findViewById(R.id.layout_other);

        //照片
        im_chat_poto = (ImageView) findViewById(R.id.im_chat_poto);
        // 拍摄
        im_chat_camera = (ImageView) findViewById(R.id.im_chat_camera);
        // 小视频
        im_chat_video = (ImageView) findViewById(R.id.im_chat_video);
        // 位置
        im_chat_location = (ImageView) findViewById(R.id.im_chat_location);

        //宽度高度适配   6
        WindowManager wm = ChatActivity.this.getWindowManager();
        int width = wm.getDefaultDisplay().getWidth();
        LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        layoutParams.width = (width - PixelOrdpManager.dip2px(getBaseContext(), 16 * 5)) / 4;
        layoutParams.height = (width - PixelOrdpManager.dip2px(getBaseContext(), 16 * 5)) / 4;
        layoutParams.leftMargin = PixelOrdpManager.dip2px(getBaseContext(), 16);
        im_chat_poto.setLayoutParams(layoutParams);
        im_chat_camera.setLayoutParams(layoutParams);
        im_chat_video.setLayoutParams(layoutParams);
        im_chat_location.setLayoutParams(layoutParams);


        //加载表情的
        initViewPager();

        //显示录音图标
        img_voice.setImageResource(R.mipmap.im_chat_voice);
        //显示表情图标
        img_expression.setImageResource(R.mipmap.emoji_bw);
        //显示其他图标
        img_other.setImageResource(R.mipmap.im_chat_add_normal);

    }


    private void initData() {
        intent = getIntent();
        if (intent.getSerializableExtra("mbUserModel") != null) {
            mbUserModel = (MBUserModel) intent.getSerializableExtra("mbUserModel");
            meMBUserModel= MBUserManager.getInstance().getMBUser(LoginManager.userid);
            txt_im_object_name.setText(mbUserModel.getNickname());

//            chatAdapter = new ChatAdapter();
//            listView.setAdapter(chatAdapter);
        }

    }

    private void initListener() {
        //返回
        findViewById(R.id.img_finish).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SoftKeyboardManager.HideSoftKeyboard(v);
                finish();
            }
        });


        //键盘的点击事件
        initKeyboardListener();



//        //播放语音
//        findViewById(R.id.txt_start).setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//
//                mPlayer = new MediaPlayer();
//                try{
//                    mPlayer.setDataSource(FileName);
//                    mPlayer.prepare();
//                    mPlayer.start();
//                }catch(IOException e){
//                    Log.e("ChatActivity","播放失败");
//                }
//
//            }
//        });
    }

    /**
     * 检测Sdcard是否存在
     *
     * @return
     */
    public static boolean isExitsSdcard() {
        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
            return true;
        else
            return false;
    }

//    private PowerManager.WakeLock wakeLock;

    /**
     * 按住说话listener
     */
    //语音操作对象
    private MediaPlayer mPlayer = null;
    private MediaRecorder mRecorder = null;
    //语音文件保存路径
    private String FileName = null;
    class PressToSpeakListen implements View.OnTouchListener {
        @SuppressLint({"ClickableViewAccessibility", "Wakelock"})
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    //语音的显示
                    layout_voice.setVisibility(View.VISIBLE);
                    //语音按钮点下
                    txt_record.setText("松开结束");
                    txt_record.setBackgroundResource(R.drawable.shape_im_chat_voice_down);

                    if (!isExitsSdcard()) {
                        Toast.makeText(ChatActivity.this, "发送语音需要sdcard支持!",
                                Toast.LENGTH_SHORT).show();
                        return false;
                    }
//                    try {
//                        v.setPressed(true);
////                        wakeLock.acquire();
////                        if (VoicePlayClickListener.isPlaying)
////                            VoicePlayClickListener.currentPlayListener
////                                    .stopPlayVoice();
////                        recordingContainer.setVisibility(View.VISIBLE);
////                        recordingHint
////                                .setText(getString(R.string.move_up_to_cancel));
////                        recordingHint.setBackgroundColor(Color.TRANSPARENT);
////                        voiceRecorder.startRecording(null, toChatUsername,
////                                getApplicationContext());
//                    } catch (Exception e) {
//                        e.printStackTrace();
//                        v.setPressed(false);
////                        if (wakeLock.isHeld())
////                            wakeLock.release();
////                        if (voiceRecorder != null)
////                            voiceRecorder.discardRecording();
////                        recordingContainer.setVisibility(View.INVISIBLE);
////                        Toast.makeText(ChatActivity.this, R.string.recoding_fail,
////                                Toast.LENGTH_SHORT).show();
//                        return false;
//                    }
                    //设置sdcard的路径
                    FileName = Environment.getExternalStorageDirectory().getAbsolutePath();
                    FileName += "/audiorecordtest.3gp";
                    mRecorder = new MediaRecorder();
                    mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                    mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
                    mRecorder.setOutputFile(FileName);
                    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
                    try {
                        mRecorder.prepare();
                    } catch (IOException e) {
                        Log.e("ChatActivity", "prepare() failed");
                    }
                    mRecorder.start();


                    return true;
                case MotionEvent.ACTION_MOVE: {
                    if (event.getY() < 0) {
                        txt_im_microphone_show_text.setText("松指取消发送");

//                        txt_record.setText("松开结束");
//                        txt_record.setBackgroundResource(R.drawable.shape_im_chat_voice_down);
                    } else {
                        txt_im_microphone_show_text.setText("上滑取消发送");

//                        txt_record.setText("按住说话");
//                        txt_record.setBackgroundResource(R.drawable.shape_im_chat_voice_up);
                    }
                    return true;
                }
                case MotionEvent.ACTION_UP:
                    //语音的显示
                    layout_voice.setVisibility(View.GONE);
                    //语音按钮点下
                    txt_record.setText("按住说话");
                    txt_record.setBackgroundResource(R.drawable.shape_im_chat_voice_up);

//                    v.setPressed(false);
////                    recordingContainer.setVisibility(View.INVISIBLE);
////                    if (wakeLock.isHeld())
////                        wakeLock.release();
//                    if (event.getY() < 0) {
//                        // discard the recorded audio.
////                        voiceRecorder.discardRecording();
//
//                    } else {
//                        // stop recording and send voice file
////                        try {
////                            int length = voiceRecorder.stopRecoding();
////                            if (length > 0) {
////                                sendVoice(voiceRecorder.getVoiceFilePath(),
////                                        voiceRecorder
////                                                .getVoiceFileName(toChatUsername),
////                                        Integer.toString(length), false);
////                            } else if (length == EMError.INVALID_FILE) {
////                                Toast.makeText(getApplicationContext(), "无录音权限",
////                                        Toast.LENGTH_SHORT).show();
////                            } else {
////                                Toast.makeText(getApplicationContext(), "录音时间太短",
////                                        Toast.LENGTH_SHORT).show();
////                            }
////                        } catch (Exception e) {
////                            e.printStackTrace();
////                            Toast.makeText(ChatActivity.this, "发送失败,请检测服务器是否连接",
////                                    Toast.LENGTH_SHORT).show();
////                        }
//
//                    }

                    mRecorder.stop();
                    mRecorder.release();
                    mRecorder = null;

                    return true;
                default:
//                    recordingContainer.setVisibility(View.INVISIBLE);
//                    if (voiceRecorder != null)
//                        voiceRecorder.discardRecording();
                    return false;
            }
        }
    }


    private void initKeyboardListener() {

        //录音的点击
        txt_record.setOnTouchListener(new PressToSpeakListen());

        //照片
        im_chat_poto.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);

                startActivityForResult(intent, CameraConstants.REQUEST_PICK);

            }
        });
        // 拍摄
        im_chat_camera.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (!(ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)) {
                    ActivityCompat.requestPermissions(ChatActivity.this, new String[]{Manifest.permission.CAMERA}, REQUEST_CODE_CAMERA_PERMISSION);
                } else {
                    startCamera();
                }

            }
        });
        // 小视频
        im_chat_video.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
        // 位置
        im_chat_location.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                aMap.getMapScreenShot(new AMap.OnMapScreenShotListener() {
                    @Override
                    public void onMapScreenShot(Bitmap bitmap) {

                        img_location_map.setImageBitmap(bitmap);
                        SystemClock.sleep(100);

                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                //照片
                                Bitmap bitmapFinish = getBitmapByView(scrollView_location_bitmap);
                                //照片路径
                                String filePath = savePic(bitmapFinish);
                                //经纬度     latitude;  longitude;
                                //位置的描述  currentSpecificPosition
//                                img_bitmap.setImageBitmap(bitmapFinish);
                                Log.d("ChatActivity", filePath);
                                Log.d("ChatActivity", "latitude:" + latitude);
                                Log.d("ChatActivity", "longitude:" + longitude);
                                Log.d("ChatActivity", currentSpecificPosition);
                                // TODO: 2016/8/13  位置的信息



                            }
                        });

                    }

                    @Override
                    public void onMapScreenShot(Bitmap bitmap, int i) {

                    }
                });

            }
        });


        //键盘上边区域被点击键盘收回
        listView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                currentSelect = 0;
                SoftKeyboardManager.HideSoftKeyboard(v);
                //显示录音图标
                img_voice.setImageResource(R.mipmap.im_chat_voice);
                //显示表情图标
                img_expression.setImageResource(R.mipmap.emoji_bw);
                //显示其他图标
                img_other.setImageResource(R.mipmap.im_chat_add_normal);

                //表情  其他隐藏
                layout_expression.setVisibility(View.GONE);
                layout_other.setVisibility(View.GONE);

                txt_im_message.setVisibility(View.VISIBLE);
                txt_record.setVisibility(View.GONE);

                return false;
            }
        });
        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                currentSelect = 0;
                SoftKeyboardManager.HideSoftKeyboard(view);
                //显示录音图标
                img_voice.setImageResource(R.mipmap.im_chat_voice);
                //显示表情图标
                img_expression.setImageResource(R.mipmap.emoji_bw);
                //显示其他图标
                img_other.setImageResource(R.mipmap.im_chat_add_normal);

                //表情  其他隐藏
                layout_expression.setVisibility(View.GONE);
                layout_other.setVisibility(View.GONE);

                txt_im_message.setVisibility(View.VISIBLE);
                txt_record.setVisibility(View.GONE);

            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            }
        });
        //评论内容被点击时viewpager
        txt_im_message.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                layout_expression.setVisibility(View.GONE);
                isShowExpressionViewpager = false;
                return false;
            }
        });

        //录音
        img_voice.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (isShowVoice == false) {

                    //显示键盘图标
                    img_voice.setImageResource(R.mipmap.im_chat_keyboard);
                    currentSelect = 1;
                    //显示录音
                    txt_record.setVisibility(View.VISIBLE);
                    //隐藏输入框
                    txt_im_message.setVisibility(View.GONE);
                    isShowVoice = true;
                    SoftKeyboardManager.HideSoftKeyboard(v);

                } else if (isShowVoice == true) {

                    //显示录音图标
                    img_voice.setImageResource(R.mipmap.im_chat_voice);
                    currentSelect = 0;
                    //隐藏录音
                    txt_record.setVisibility(View.GONE);
                    //显示输入框
                    txt_im_message.setVisibility(View.VISIBLE);
                    isShowVoice = false;
                    SoftKeyboardManager.ShowSoftKeyboard(v, ChatActivity.this);

                }

                //隐藏表情
                layout_expression.setVisibility(View.GONE);
                // 隐藏其他
                layout_other.setVisibility(View.GONE);


//                //显示录音图标
//                img_voice.setImageResource(R.mipmap.im_chat_voice);
                //显示表情图标
                img_expression.setImageResource(R.mipmap.emoji_bw);
                //显示其他图标
                img_other.setImageResource(R.mipmap.im_chat_add_normal);

            }
        });

        //表情按钮
        img_expression.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //显示输入框
                txt_im_message.setVisibility(View.VISIBLE);

                if (isShowExpressionViewpager == true) {

                    //显示表情图标
                    img_expression.setImageResource(R.mipmap.emoji_bw);

                    currentSelect = 0;
                    //隐藏表情输入框
                    layout_expression.setVisibility(View.GONE);
                    isShowExpressionViewpager = false;
                    SoftKeyboardManager.ShowSoftKeyboard(v, ChatActivity.this);
                } else {

                    //显示键盘图标
                    img_expression.setImageResource(R.mipmap.im_chat_keyboard);

                    currentSelect = 2;
                    //显示表情输入框
//                    layout_expression.setVisibility(View.VISIBLE);
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            SystemClock.sleep(200);
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    layout_expression.setVisibility(View.VISIBLE);
                                }
                            });

                        }
                    }).start();

                    isShowExpressionViewpager = true;
                    SoftKeyboardManager.HideSoftKeyboard(v);
                }

                //隐藏录音
                txt_record.setVisibility(View.GONE);
                // 隐藏其他
                layout_other.setVisibility(View.GONE);


                //显示录音图标
                img_voice.setImageResource(R.mipmap.im_chat_voice);
                //显示表情图标
//                img_expression.setImageResource(R.mipmap.emoji_bw);
                //显示其他图标
                img_other.setImageResource(R.mipmap.im_chat_add_normal);

            }
        });

        //其他
        img_other.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //显示输入框
                txt_im_message.setVisibility(View.VISIBLE);

                if (isShowOther == false) {

                    //显示键盘图标
                    img_other.setImageResource(R.mipmap.im_chat_keyboard);

                    currentSelect = 3;
                    //显示其他
//                    layout_other.setVisibility(View.VISIBLE);
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            SystemClock.sleep(200);
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    layout_other.setVisibility(View.VISIBLE);
                                }
                            });

                        }
                    }).start();

                    isShowOther = true;
                    SoftKeyboardManager.HideSoftKeyboard(v);
                } else if (isShowOther == true) {

                    //显示其他图标
                    img_other.setImageResource(R.mipmap.im_chat_add_normal);

                    currentSelect = 0;
                    //隐藏其他
                    layout_other.setVisibility(View.GONE);
                    isShowOther = false;
                    SoftKeyboardManager.ShowSoftKeyboard(v, ChatActivity.this);
                }

                //隐藏录音
                txt_record.setVisibility(View.GONE);
                // 隐藏表情
                layout_expression.setVisibility(View.GONE);
                //显示录音图标
                img_voice.setImageResource(R.mipmap.im_chat_voice);
                //显示表情图标
                img_expression.setImageResource(R.mipmap.emoji_bw);
                //显示其他图标
//                img_other.setImageResource(R.mipmap.im_chat_add_normal);

            }
        });

        //判断软键盘显示隐藏
        keyboardListenRelativeLayout.setOnKeyboardStateChangedListener(new KeyboardListenRelativeLayout.IOnKeyboardStateChangedListener() {
            @Override
            public void onKeyboardStateChanged(int state) {

                switch (state) {
                    case KeyboardListenRelativeLayout.KEYBOARD_STATE_HIDE://软键盘隐藏
                        if (currentSelect == 0) {
                            //没有点击  录音  表情  其他
                            txt_im_message.setVisibility(View.VISIBLE);
                            txt_record.setVisibility(View.GONE);
                            layout_expression.setVisibility(View.GONE);
                            layout_other.setVisibility(View.GONE);

                            //语音
                            isShowVoice = false;
                            //表情
                            isShowExpressionViewpager = false;
                            //其他
                            isShowOther = false;

                        } else if (currentSelect == 1) {
                            //点击录音
                            txt_im_message.setVisibility(View.GONE);
                            txt_record.setVisibility(View.VISIBLE);
                            layout_expression.setVisibility(View.GONE);
                            layout_other.setVisibility(View.GONE);

                            //语音
                            isShowVoice = false;
                            //表情
                            isShowExpressionViewpager = false;
                            //其他
                            isShowOther = false;

                        } else if (currentSelect == 2) {
                            //点击表情
                            txt_im_message.setVisibility(View.VISIBLE);
                            txt_record.setVisibility(View.GONE);

                            layout_expression.setVisibility(View.VISIBLE);

                            layout_other.setVisibility(View.GONE);
                            //表情
                            isShowExpressionViewpager = true;
                            //语音
                            isShowVoice = false;
                            //其他
                            isShowOther = false;

                        } else if (currentSelect == 3) {
                            //点击其他
                            txt_im_message.setVisibility(View.VISIBLE);
                            txt_record.setVisibility(View.GONE);
                            layout_expression.setVisibility(View.GONE);
                            layout_other.setVisibility(View.VISIBLE);
                            //其他
                            isShowOther = true;
                            //语音
                            isShowVoice = false;
                            //表情
                            isShowExpressionViewpager = false;

                        }

                        break;
                    case KeyboardListenRelativeLayout.KEYBOARD_STATE_SHOW://软键盘显示

                        //隐藏录音
                        txt_record.setVisibility(View.GONE);
                        // 隐藏表情
                        layout_expression.setVisibility(View.GONE);
                        //隐藏其他
                        layout_other.setVisibility(View.GONE);

                        isShowExpressionViewpager = false;
                        isShowVoice = false;
                        isShowOther = false;
                        break;
                    default:
                        break;
                }

            }
        });


        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {

                switch (scrollState) {
                    // 当不滚动时
                    case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
                    case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
                    case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
                        SoftKeyboardManager.HideSoftKeyboard(view);
                        layout_expression.setVisibility(View.GONE);
                        isShowExpressionViewpager = false;
                        break;


                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            }
        });


    }

    //聊天适配器
    private class ChatAdapter extends BaseAdapter {

        //类型
        //聊天对象文字
        private int TypeObjectCharacter = 0;
        // 语音
        private int TypeObjectVoice = 1;
        // 照片
        private int TypeObjectImg = 2;
        // 位置
        private int TypeObjectLocation = 3;

        //自己文字
        private int TypeMeCharacter = 4;
        // 语音
        private int TypeMeVoice = 5;
        // 照片
        private int TypeMeImg = 6;
        // 位置
        private int TypeMeLocation = 7;

        @Override
        public int getCount() {
            return 100;
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public int getItemViewType(int position) {
//            return super.getItemViewType(position);
            if (position % 11 == 0) {
                return TypeObjectCharacter;
            } else if (position % 9 == 0) {
                return TypeObjectVoice;
            } else if (position % 8 == 0) {
                return TypeObjectImg;
            } else if (position % 7 == 0) {
                return TypeObjectLocation;
            } else if (position % 5 == 0) {
                return TypeMeCharacter;
            } else if (position % 3 == 0) {
                return TypeMeVoice;
            } else if (position % 2 == 0) {
                return TypeMeImg;
            } else {
                return TypeMeLocation;
            }

        }

        @Override
        public int getViewTypeCount() {
            return 8;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ObjectCharacterViewHolder objectCharacterViewHolder = null;
            ObjectVoiceViewHolder objectVoiceViewHolder = null;
            ObjectImgViewHolder objectImgViewHolder = null;
            ObjectLocationViewHolder objectLocationViewHolder = null;
            MeCharacterViewHolder meCharacterViewHolder = null;
            MeVoiceViewHolder meVoiceViewHolder = null;
            MeImgViewHolder meImgViewHolder = null;
            MeLocationViewHolder meLocationViewHolder = null;

            int Type = getItemViewType(position);
            if (convertView == null) {

                if (Type == TypeObjectCharacter) {
                    convertView = getLayoutInflater().inflate(R.layout.inflate_im_chat_object_character, parent, false);
                    objectCharacterViewHolder = new ObjectCharacterViewHolder();
                    objectCharacterViewHolder.txt_im_chat_time = (TextView) convertView.findViewById(R.id.txt_im_chat_time);
                    objectCharacterViewHolder.img_head_portrait = (RoundImageView) convertView.findViewById(R.id.img_head_portrait);
                    objectCharacterViewHolder.txt_im_chat_object_character = (TextView) convertView.findViewById(R.id.txt_im_chat_object_character);
                    convertView.setTag(objectCharacterViewHolder);
                } else if (Type == TypeObjectVoice) {
                    convertView = getLayoutInflater().inflate(R.layout.inflate_im_chat_object_voice, parent, false);
                    objectVoiceViewHolder = new ObjectVoiceViewHolder();
                    objectVoiceViewHolder.txt_im_chat_time = (TextView) convertView.findViewById(R.id.txt_im_chat_time);
                    objectVoiceViewHolder.img_head_portrait = (RoundImageView) convertView.findViewById(R.id.img_head_portrait);
                    objectVoiceViewHolder.txt_im_chat_object_voice = (TextView) convertView.findViewById(R.id.txt_im_chat_object_voice);
                    objectVoiceViewHolder.img_im_chat_object_voice = (ImageView) convertView.findViewById(R.id.img_im_chat_object_voice);
                    objectVoiceViewHolder.txt_im_chat_object_voice_seconds = (TextView) convertView.findViewById(R.id.txt_im_chat_object_voice_seconds);
                    objectVoiceViewHolder.img_im_chat_object_voice_read_or_noread = (ImageView) convertView.findViewById(R.id.img_im_chat_object_voice_read_or_noread);
                    convertView.setTag(objectVoiceViewHolder);
                } else if (Type == TypeObjectImg) {
                    convertView = getLayoutInflater().inflate(R.layout.inflate_im_chat_object_img, parent, false);
                    objectImgViewHolder = new ObjectImgViewHolder();
                    objectImgViewHolder.txt_im_chat_time = (TextView) convertView.findViewById(R.id.txt_im_chat_time);
                    objectImgViewHolder.img_head_portrait = (RoundImageView) convertView.findViewById(R.id.img_head_portrait);
                    objectImgViewHolder.img_im_chat_object_img = (ImageView) convertView.findViewById(R.id.img_im_chat_object_img);
                    convertView.setTag(objectImgViewHolder);
                } else if (Type == TypeObjectLocation) {
                    convertView = getLayoutInflater().inflate(R.layout.inflate_im_chat_object_location, parent, false);
                    objectLocationViewHolder = new ObjectLocationViewHolder();
                    objectLocationViewHolder.txt_im_chat_time = (TextView) convertView.findViewById(R.id.txt_im_chat_time);
                    objectLocationViewHolder.img_head_portrait = (RoundImageView) convertView.findViewById(R.id.img_head_portrait);
                    objectLocationViewHolder.img_im_chat_object_location = (ImageView) convertView.findViewById(R.id.img_im_chat_object_location);
                    convertView.setTag(objectLocationViewHolder);
                } else if (Type == TypeMeCharacter) {
                    convertView = getLayoutInflater().inflate(R.layout.inflate_im_chat_me_character, parent, false);
                    meCharacterViewHolder = new MeCharacterViewHolder();
                    meCharacterViewHolder.txt_im_chat_time = (TextView) convertView.findViewById(R.id.txt_im_chat_time);
                    meCharacterViewHolder.img_head_portrait = (RoundImageView) convertView.findViewById(R.id.img_head_portrait);
                    meCharacterViewHolder.txt_im_chat_me_character = (TextView) convertView.findViewById(R.id.txt_im_chat_me_character);
                    convertView.setTag(meCharacterViewHolder);
                } else if (Type == TypeMeVoice) {
                    convertView = getLayoutInflater().inflate(R.layout.inflate_im_chat_me_voice, parent, false);
                    meVoiceViewHolder = new MeVoiceViewHolder();
                    meVoiceViewHolder.txt_im_chat_time = (TextView) convertView.findViewById(R.id.txt_im_chat_time);
                    meVoiceViewHolder.img_head_portrait = (RoundImageView) convertView.findViewById(R.id.img_head_portrait);
                    meVoiceViewHolder.txt_im_chat_me_voice = (TextView) convertView.findViewById(R.id.txt_im_chat_me_voice);
                    meVoiceViewHolder.img_im_chat_me_voice = (ImageView) convertView.findViewById(R.id.img_im_chat_me_voice);
                    meVoiceViewHolder.txt_im_chat_me_voice_seconds = (TextView) convertView.findViewById(R.id.txt_im_chat_me_voice_seconds);
                    meVoiceViewHolder.img_im_chat_me_voice_read_or_noread = (ImageView) convertView.findViewById(R.id.img_im_chat_me_voice_read_or_noread);
                    convertView.setTag(meVoiceViewHolder);
                } else if (Type == TypeMeImg) {
                    convertView = getLayoutInflater().inflate(R.layout.inflate_im_chat_me_img, parent, false);
                    meImgViewHolder = new MeImgViewHolder();
                    meImgViewHolder.txt_im_chat_time = (TextView) convertView.findViewById(R.id.txt_im_chat_time);
                    meImgViewHolder.img_head_portrait = (RoundImageView) convertView.findViewById(R.id.img_head_portrait);
                    meImgViewHolder.img_im_chat_me_img = (ImageView) convertView.findViewById(R.id.img_im_chat_me_img);
                    convertView.setTag(meImgViewHolder);
                } else if (Type == TypeMeLocation) {
                    convertView = getLayoutInflater().inflate(R.layout.inflate_im_chat_me_location, parent, false);
                    meLocationViewHolder = new MeLocationViewHolder();
                    meLocationViewHolder.txt_im_chat_time = (TextView) convertView.findViewById(R.id.txt_im_chat_time);
                    meLocationViewHolder.img_head_portrait = (RoundImageView) convertView.findViewById(R.id.img_head_portrait);
                    meLocationViewHolder.img_im_chat_me_location = (ImageView) convertView.findViewById(R.id.img_im_chat_me_location);
                    convertView.setTag(meLocationViewHolder);
                }

            } else {

                if (Type == TypeObjectCharacter) {
                    objectCharacterViewHolder = (ObjectCharacterViewHolder) convertView.getTag();
                } else if (Type == TypeObjectVoice) {
                    objectVoiceViewHolder = (ObjectVoiceViewHolder) convertView.getTag();
                } else if (Type == TypeObjectImg) {
                    objectImgViewHolder = (ObjectImgViewHolder) convertView.getTag();
                } else if (Type == TypeObjectLocation) {
                    objectLocationViewHolder = (ObjectLocationViewHolder) convertView.getTag();
                } else if (Type == TypeMeCharacter) {
                    meCharacterViewHolder = (MeCharacterViewHolder) convertView.getTag();
                } else if (Type == TypeMeVoice) {
                    meVoiceViewHolder = (MeVoiceViewHolder) convertView.getTag();
                } else if (Type == TypeMeImg) {
                    meImgViewHolder = (MeImgViewHolder) convertView.getTag();
                } else if (Type == TypeMeLocation) {
                    meLocationViewHolder = (MeLocationViewHolder) convertView.getTag();
                }

            }


            //设置数据
            if (Type == TypeObjectCharacter) {
                setObjectCharacterData(objectCharacterViewHolder);
            } else if (Type == TypeObjectVoice) {
                setObjectVoiceData(objectVoiceViewHolder);
            } else if (Type == TypeObjectImg) {
                setObjectImgData(objectImgViewHolder);
            } else if (Type == TypeObjectLocation) {
                setObjectLocationData(objectLocationViewHolder);
            } else if (Type == TypeMeCharacter) {
                setMeCharacterData(meCharacterViewHolder);
            } else if (Type == TypeMeVoice) {
                setMeVoiceData(meVoiceViewHolder);
            } else if (Type == TypeMeImg) {
                setMeImgData(meImgViewHolder);
            } else if (Type == TypeMeLocation) {
                setMeLocationData(meLocationViewHolder);
            }


            return convertView;
        }

        //设置对象文字
        private void setObjectCharacterData(ObjectCharacterViewHolder objectCharacterViewHolder) {

            objectCharacterViewHolder.txt_im_chat_time.setText("2013111");
            Picasso.with(ChatActivity.this).load(mbUserModel.getHeadurl() + "!thumb").into(objectCharacterViewHolder.img_head_portrait);
            objectCharacterViewHolder.txt_im_chat_object_character.setText("哈哈哈老客户卡哈斯的发生哈哈镜和司法斯蒂芬回来好伐啦开始的发生");

        }

        //设置对象语音
        private void setObjectVoiceData(ObjectVoiceViewHolder objectVoiceViewHolder) {
            objectVoiceViewHolder.txt_im_chat_time.setText("2013111");
            Picasso.with(ChatActivity.this).load(mbUserModel.getHeadurl() + "!thumb").into(objectVoiceViewHolder.img_head_portrait);
        }


        //设置对象的照片
        private void setObjectImgData(ObjectImgViewHolder objectImgViewHolder) {
            objectImgViewHolder.txt_im_chat_time.setText("32123132");
            Picasso.with(ChatActivity.this).load(mbUserModel.getHeadurl() + "!thumb").into(objectImgViewHolder.img_head_portrait);
        }

        //设置对象位置的
        private void setObjectLocationData(ObjectLocationViewHolder objectLocationViewHolder) {
            objectLocationViewHolder.txt_im_chat_time.setText("32123132");
            Picasso.with(ChatActivity.this).load(mbUserModel.getHeadurl() + "!thumb").into(objectLocationViewHolder.img_head_portrait);
        }


        //设置自己的文字
        private void setMeCharacterData(MeCharacterViewHolder meCharacterViewHolder) {
            meCharacterViewHolder.txt_im_chat_time.setText("465654564");
            Picasso.with(ChatActivity.this).load(meMBUserModel.getHeadurl() + "!thumb").into(meCharacterViewHolder.img_head_portrait);
            meCharacterViewHolder.txt_im_chat_me_character.setText("离开家电费卡坚实的法律会计师");
        }

        //设置自己的语音
        private void setMeVoiceData(MeVoiceViewHolder meVoiceViewHolder) {
            meVoiceViewHolder.txt_im_chat_time.setText("465654564");
            Picasso.with(ChatActivity.this).load(meMBUserModel.getHeadurl() + "!thumb").into(meVoiceViewHolder.img_head_portrait);
//            meVoiceViewHolder.img_im_chat_me_voice

        }

        //设置自己的照片
        private void setMeImgData(MeImgViewHolder meImgViewHolder) {
            meImgViewHolder.txt_im_chat_time.setText("32123132");
            Picasso.with(ChatActivity.this).load(meMBUserModel.getHeadurl() + "!thumb").into(meImgViewHolder.img_head_portrait);
//            meImgViewHolder.img_im_chat_me_img
        }

        //设置自己的位置
        private void setMeLocationData(MeLocationViewHolder meLocationViewHolder) {
            meLocationViewHolder.txt_im_chat_time.setText("51321321");
            Picasso.with(ChatActivity.this).load(meMBUserModel.getHeadurl() + "!thumb").into(meLocationViewHolder.img_head_portrait);
//            meLocationViewHolder.img_im_chat_me_location

        }


        //聊天对象文字
        private class ObjectCharacterViewHolder {
            private TextView txt_im_chat_time;
            private RoundImageView img_head_portrait;
            private TextView txt_im_chat_object_character;

        }

        // 语音
        private class ObjectVoiceViewHolder {
            private TextView txt_im_chat_time;
            private RoundImageView img_head_portrait;
            private TextView txt_im_chat_object_voice;
            private ImageView img_im_chat_object_voice;
            private TextView txt_im_chat_object_voice_seconds;
            private ImageView img_im_chat_object_voice_read_or_noread;
        }

        // 照片
        private class ObjectImgViewHolder {
            private TextView txt_im_chat_time;
            private RoundImageView img_head_portrait;
            private ImageView img_im_chat_object_img;
        }

        // 位置
        private class ObjectLocationViewHolder {
            private TextView txt_im_chat_time;
            private RoundImageView img_head_portrait;
            private ImageView img_im_chat_object_location;
        }

        //自己文字
        private class MeCharacterViewHolder {
            private TextView txt_im_chat_time;
            private RoundImageView img_head_portrait;
            private TextView txt_im_chat_me_character;
        }

        // 语音
        private class MeVoiceViewHolder {
            private TextView txt_im_chat_time;
            private RoundImageView img_head_portrait;
            private TextView txt_im_chat_me_voice;
            private ImageView img_im_chat_me_voice;
            private TextView txt_im_chat_me_voice_seconds;
            private ImageView img_im_chat_me_voice_read_or_noread;
        }

        // 照片
        private class MeImgViewHolder {
            private TextView txt_im_chat_time;
            private RoundImageView img_head_portrait;
            private ImageView img_im_chat_me_img;
        }

        // 位置
        private class MeLocationViewHolder {
            private TextView txt_im_chat_time;
            private RoundImageView img_head_portrait;
            private ImageView img_im_chat_me_location;
        }

    }

    private void initViewPager() {

        viewsList = new ArrayList<View>();
        View view1 = LayoutInflater.from(this).inflate(R.layout.inflate_expression_one, null);
        View view2 = LayoutInflater.from(this).inflate(R.layout.inflate_expression_two, null);
        View view3 = LayoutInflater.from(this).inflate(R.layout.inflate_expression_there, null);

        viewsList.add(view1);
        viewsList.add(view2);
        viewsList.add(view3);


        //添加小圆点的图片
        imageViews = new ImageView[viewsList.size()];
        for (int i = 0; i < viewsList.size(); i++) {
            imageView = new ImageView(this);
            //设置小圆点imageview的参数
            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(20, 20);
            layoutParams.setMargins(10, 0, 10, 0);
//            imageView.setLayoutParams(new ViewGroup.LayoutParams(20,20));//创建一个宽高均为20 的布局
            imageView.setLayoutParams(layoutParams);//创建一个宽高均为20 的布局
            imageView.setPadding(20, 0, 20, 0);
            //默认选中的是第一张图片,此时第一个小圆点是选中状态,其他不是
            if (i == 0) {
                imageView.setBackgroundResource(R.drawable.shape_expression_click);
            } else {
                imageView.setBackgroundResource(R.drawable.shape_expression_unclick);
            }
            //将imageviews添加到小圆点视图组
            viewPoints.addView(imageView);
            imageViews[i] = imageView;

        }
        viewPagerAdapter = new ViewPagerAdapter(viewsList);
        viewPager.setAdapter(viewPagerAdapter);
        viewPager.setOnPageChangeListener(new GuidePageChangeListener());

        //设置第一组表情
        initGridViewExpresionOne(view1);
        //设置第二组表情
        initGridViewExpresionTwo(view2);
        //设置第三组表情
        initGridViewExpresionThere(view3);
    }

    private String getEmojiStringByUnicode(int unicode) {
        return new String(Character.toChars(unicode));
    }

    //键盘
    private void initGridViewExpresionOne(View view) {
        myGridViewExpresionOne = (MyGridView) view.findViewById(R.id.gridview_expression);
        gridAdapter = new GridAdapter(ChatActivity.this);
        unicodeOne.add(0, 0x1F604);
        unicodeOne.add(1, 0x1F60A);
        unicodeOne.add(2, 0x1F603);
        unicodeOne.add(3, 0x263A);
        unicodeOne.add(4, 0x1F609);
        unicodeOne.add(5, 0x1F60D);
        unicodeOne.add(6, 0x1F618);
        unicodeOne.add(7, 0x1F61A);
        unicodeOne.add(8, 0x1F633);
        unicodeOne.add(9, 0x1F60C);
        unicodeOne.add(10, 0x1F601);
        unicodeOne.add(11, 0x1F61C);
        unicodeOne.add(12, 0x1F61D);
        unicodeOne.add(13, 0x1F612);
        unicodeOne.add(14, 0x1F60F);
        unicodeOne.add(15, 0x1F613);
        unicodeOne.add(16, 0x1F614);
        unicodeOne.add(17, 0x1F61E);
        unicodeOne.add(18, 0x1F616);
        unicodeOne.add(19, 0x1F625);
        unicodeOne.add(20, 0x1F630);
        unicodeOne.add(21, 0x1F628);
        unicodeOne.add(22, 0x1F623);
        unicodeOne.add(23, 0x1F622);
        unicodeOne.add(24, 0x1F62D);
        unicodeOne.add(25, 0x1F602);
        unicodeOne.add(26, 0x1F632);
        unicodeOne.add(27, 0x1F631);


        myGridViewExpresionOne.setAdapter(gridAdapter);
        gridAdapter.setDataToAdapter(unicodeOne);
        gridAdapter.notifyDataSetChanged();

        //点击选择是那一项
        myGridViewExpresionOne.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                if (position == 27) {
//                    Toast.makeText(DynamicDetailActivity.this, "删除" + position, Toast.LENGTH_SHORT).show();
                    if (txt_im_message.getText().toString().length() > 0) {
                        if (txt_im_message.getText().toString().length() == 1) {
                            String commentString = txt_im_message.getText().toString().substring(0,
                                    txt_im_message.getText().toString().length() - 1);
                            txt_im_message.setText(commentString);
                        } else {
                            int lastIndex = txt_im_message.getText().toString().lastIndexOf("�");
                            if (lastIndex == -1) {
                                //判断当前的是不是表情
                                String commentString = txt_im_message.getText().toString().substring(0,
                                        txt_im_message.getText().toString().length() - 2);
                                txt_im_message.setText(commentString);
                            } else {
                                //判断当前的是不是表情
                                String commentString = txt_im_message.getText().toString().substring(0,
                                        txt_im_message.getText().toString().length() - 1);
                                txt_im_message.setText(commentString);
                            }
                        }

                    }

                } else {
                    int unicodeJoy = unicodeOne.get(position);
                    String emojiString = getEmojiStringByUnicode(unicodeJoy);
                    txt_im_message.setText(txt_im_message.getText().toString().trim() + emojiString);
                }

                txt_im_message.setSelection(txt_im_message.getText().length());

            }
        });


    }

    private void initGridViewExpresionTwo(View view) {

        myGridViewExpresionTwo = (MyGridView) view.findViewById(R.id.gridview_expression);
        gridAdapter = new GridAdapter(ChatActivity.this);
        unicodeTwo.add(0, 0x1F631);
        unicodeTwo.add(1, 0x1F620);
        unicodeTwo.add(2, 0x1F621);
        unicodeTwo.add(3, 0x1F62A);
        unicodeTwo.add(4, 0x1F637);
        unicodeTwo.add(5, 0x1F47F);
        unicodeTwo.add(6, 0x1F47D);
        unicodeTwo.add(7, 0x1F49B);
        unicodeTwo.add(8, 0x1F499);
        unicodeTwo.add(9, 0x1F49C);
        unicodeTwo.add(10, 0x1F497);
        unicodeTwo.add(11, 0x1F49A);
        unicodeTwo.add(12, 0x2764);
        unicodeTwo.add(13, 0x1F494);
        unicodeTwo.add(14, 0x1F493);
        unicodeTwo.add(15, 0x1F498);
        unicodeTwo.add(16, 0x2728);
        unicodeTwo.add(17, 0x2B50);
        unicodeTwo.add(18, 0x1F31F);
        unicodeTwo.add(19, 0x1F4A2);
        unicodeTwo.add(20, 0x2757);
        unicodeTwo.add(21, 0x2755);
        unicodeTwo.add(22, 0x2753);
        unicodeTwo.add(23, 0x2754);
        unicodeTwo.add(24, 0x1F4A4);
        unicodeTwo.add(25, 0x1F4A8);
        unicodeTwo.add(26, 0x1F4A6);
        unicodeTwo.add(27, 0x1F631);


        myGridViewExpresionTwo.setAdapter(gridAdapter);
        gridAdapter.setDataToAdapter(unicodeTwo);
        gridAdapter.notifyDataSetChanged();

        //点击选择是那一项
        myGridViewExpresionTwo.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                if (position == 27) {
                    if (txt_im_message.getText().toString().length() > 0) {
                        if (txt_im_message.getText().toString().length() == 1) {
                            String commentString = txt_im_message.getText().toString().substring(0,
                                    txt_im_message.getText().toString().length() - 1);
                            txt_im_message.setText(commentString);
                        } else {
                            int lastIndex = txt_im_message.getText().toString().lastIndexOf("�");
                            if (lastIndex == -1) {
                                //判断当前的是不是表情
                                String commentString = txt_im_message.getText().toString().substring(0,
                                        txt_im_message.getText().toString().length() - 2);
                                txt_im_message.setText(commentString);
                            } else {
                                //判断当前的是不是表情
                                String commentString = txt_im_message.getText().toString().substring(0,
                                        txt_im_message.getText().toString().length() - 1);
                                txt_im_message.setText(commentString);
                            }
                        }


                    }
                } else {

                    int unicodeJoy = unicodeTwo.get(position);
                    String emojiString = getEmojiStringByUnicode(unicodeJoy);
                    txt_im_message.setText(txt_im_message.getText().toString().trim() + emojiString);
                }

                txt_im_message.setSelection(txt_im_message.getText().length());

            }
        });

    }

    private void initGridViewExpresionThere(View view) {

        myGridViewExpresionThere = (MyGridView) view.findViewById(R.id.gridview_expression);
        gridAdapter = new GridAdapter(ChatActivity.this);
        unicodeThere.add(0, 0x1F3B6);
        unicodeThere.add(1, 0x1F3B5);
        unicodeThere.add(2, 0x1F525);
        unicodeThere.add(3, 0x1F4A9);
        unicodeThere.add(4, 0x1F44D);
        unicodeThere.add(5, 0x1F44E);
        unicodeThere.add(6, 0x1F44C);
        unicodeThere.add(7, 0x1F44A);
        unicodeThere.add(8, 0x270A);
        unicodeThere.add(9, 0x270C);
        unicodeThere.add(10, 0x1F44B);
        unicodeThere.add(11, 0x270B);
        unicodeThere.add(12, 0x1F450);
        unicodeThere.add(13, 0x1F446);
        unicodeThere.add(14, 0x1F447);
        unicodeThere.add(15, 0x1F449);
        unicodeThere.add(16, 0x1F448);
        unicodeThere.add(17, 0x1F64C);
        unicodeThere.add(18, 0x1F64F);
        unicodeThere.add(19, 0x261D);
        unicodeThere.add(20, 0x1F44F);
        unicodeThere.add(21, 0x1F4AA);
        unicodeThere.add(22, 0x1F6B6);
        unicodeThere.add(23, 0x1F3C3);
        unicodeThere.add(24, 0x1F46B);
        unicodeThere.add(25, 0x1F483);
        unicodeThere.add(26, 0x1F46F);
        unicodeThere.add(27, 0x1F631);


        myGridViewExpresionThere.setAdapter(gridAdapter);
        gridAdapter.setDataToAdapter(unicodeThere);
        gridAdapter.notifyDataSetChanged();

        //点击选择是那一项
        myGridViewExpresionThere.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                if (position == 27) {
                    if (txt_im_message.getText().toString().length() > 0) {
                        if (txt_im_message.getText().toString().length() == 1) {
                            String commentString = txt_im_message.getText().toString().substring(0,
                                    txt_im_message.getText().toString().length() - 1);
                            txt_im_message.setText(commentString);
                        } else {
                            int lastIndex = txt_im_message.getText().toString().lastIndexOf("�");
                            if (lastIndex == -1) {
                                //判断当前的是不是表情
                                String commentString = txt_im_message.getText().toString().substring(0,
                                        txt_im_message.getText().toString().length() - 2);
                                txt_im_message.setText(commentString);
                            } else {
                                //判断当前的是不是表情
                                String commentString = txt_im_message.getText().toString().substring(0,
                                        txt_im_message.getText().toString().length() - 1);
                                txt_im_message.setText(commentString);
                            }
                        }

                    }
                } else {

                    int unicodeJoy = unicodeThere.get(position);
                    String emojiString = getEmojiStringByUnicode(unicodeJoy);
                    txt_im_message.setText(txt_im_message.getText().toString().trim() + emojiString);
                }

                txt_im_message.setSelection(txt_im_message.getText().length());

            }
        });

    }


    class GuidePageChangeListener implements ViewPager.OnPageChangeListener {

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {
            for (int i = 0; i < imageViews.length; i++) {

                imageViews[position].setBackgroundResource(R.drawable.shape_expression_click);

                //不是当前选中的page,其小圆点设置为未选中的状态
                if (position != i) {

                    imageViews[i].setBackgroundResource(R.drawable.shape_expression_unclick);

                }

            }

        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    }

    //适配器
    public class ViewPagerAdapter extends PagerAdapter {

        private List<View> views;

        public ViewPagerAdapter(List<View> views) {
            this.views = views;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
//            super.destroyItem(container, position, object);
            container.removeView(views.get(position));
        }

        @Override
        public void finishUpdate(ViewGroup container) {
//            super.finishUpdate(container);
        }

        @Override
        public int getCount() {
            if (views != null) {
                return views.size();
            }
            return 0;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            container.addView(views.get(position));
            return views.get(position);
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
//            return false;
        }

    }

    //表情适配器
    private class GridAdapter extends BaseAdapter {
        private LayoutInflater layoutInflater;
        ArrayList<Integer> unicodeList = new ArrayList<Integer>();

        public void setDataToAdapter(ArrayList<Integer> picdatas) {
            if (picdatas != null) {
                unicodeList.clear();
                unicodeList.addAll(picdatas);
            }
        }

        public GridAdapter(Context context) {
            layoutInflater = LayoutInflater.from(context);
        }

        @Override
        public int getCount() {
            if (unicodeList == null) {
                return 0;
            }
            return unicodeList.size();
        }

        @Override
        public Object getItem(int position) {
            return unicodeList.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            final HodelView hodelView;
            if (convertView == null) {
                convertView = layoutInflater.inflate(R.layout.inflate_expression, parent, false);
                hodelView = new HodelView();
                hodelView.txt_expresion = (TextView) convertView.findViewById(R.id.txt_expresion);
                convertView.setTag(hodelView);
            } else {
                hodelView = (HodelView) convertView.getTag();
            }

//            设置表情数据
            if (unicodeList.size() > 0) {
                if (position < unicodeList.size() - 1) {
                    int unicodeJoy = unicodeList.get(position);
                    String emojiString = getEmojiStringByUnicode(unicodeJoy);
                    hodelView.txt_expresion.setText(emojiString);
                } else {
//                    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(PixelOrdpManager.dip2px(getBaseContext(),30),
//                            PixelOrdpManager.dip2px(getBaseContext(),20));
                    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                            ViewGroup.LayoutParams.WRAP_CONTENT);
                    params.setMargins(PixelOrdpManager.dip2px(getBaseContext(), 7),
                            PixelOrdpManager.dip2px(getBaseContext(), 12),
                            PixelOrdpManager.dip2px(getBaseContext(), 7),
                            PixelOrdpManager.dip2px(getBaseContext(), 12)
                    );
                    hodelView.txt_expresion.setLayoutParams(params);
//                    hodelView.txt_expresion.setGravity(View.TEXT_ALIGNMENT_CENTER);
                    hodelView.txt_expresion.setBackgroundResource(R.mipmap.delete_emoji);
                }

            }
            return convertView;
        }

        private String getEmojiStringByUnicode(int unicode) {
            return new String(Character.toChars(unicode));
        }

        public class HodelView {
            TextView txt_expresion;

        }

    }

    public static final int REQUEST_CODE_CAMERA_PERMISSION = 106;

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        switch (requestCode) {
            case REQUEST_CODE_CAMERA_PERMISSION:

                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    startCamera();

                } else {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
                        AlertDialog.Builder builder = new AlertDialog.Builder(this);
                        builder.setMessage("缺少“相机”权限");
                        builder.setPositiveButton("知道了", null);
                        builder.setCancelable(false);
                        builder.show();
                    } else {
                        AlertDialog.Builder builder = new AlertDialog.Builder(this);
                        builder.setMessage("缺少“相机”权限\n请到\"工具\"->\"应用\"->\"摩托邦骑行\"->\"权限\"中进行设置");
                        builder.setPositiveButton("知道了", null);
                        builder.setCancelable(false);
                        builder.show();
                    }
                }

                break;

        }
    }

    private Uri imageUri;

    private void startCamera() {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        File fileFolder = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera");
        if (!fileFolder.exists()) {
            boolean mkdirs = fileFolder.mkdirs();
            if (!mkdirs) {
                return;
            }
        }

        imageUri = Uri.fromFile(new File(fileFolder, System.currentTimeMillis() + ".jpg"));
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

        startActivityForResult(intent, CameraConstants.REQUEST_TAKE);
    }


    /***
     * 地图模块获取位置
     * @param savedInstanceState
     */

    private void initMap(Bundle savedInstanceState) {
        scrollView_location_bitmap= (ScrollView) findViewById(R.id.scrollView_location_bitmap);
        img_location_map= (ImageView) findViewById(R.id.img_location_map);
        mapView = (MapView) findViewById(R.id.map_location);
        mapView.onCreate(savedInstanceState);
        if (aMap == null) {
            aMap = mapView.getMap();
        }
        UiSettings mUiSettings = aMap.getUiSettings();
        mUiSettings.setZoomControlsEnabled(false);// 是否显示放大缩小按钮
        mUiSettings.setMyLocationButtonEnabled(false);// 是否显示定位按钮
        mUiSettings.setCompassEnabled(false);// 是否显示指南针
        mUiSettings.setScaleControlsEnabled(false); // 是否显示比例尺
        mUiSettings.setLogoPosition(AMapOptions.LOGO_POSITION_BOTTOM_LEFT);// logo位置
        /**
         * 自定义定位图标
         */
        setMyLocationStyle();
        /**
         * 设置定位资源
         */
        aMap.setLocationSource(this);
        aMap.setMyLocationEnabled(true);
        aMap.setMyLocationType(AMap.LOCATION_TYPE_LOCATE);
        aMap.moveCamera(CameraUpdateFactory.zoomTo(16));
        getLocation();
        //准确位置获取
        geocodeSearch = new GeocodeSearch(ChatActivity.this);
        geocodeSearch.setOnGeocodeSearchListener(ChatActivity.this);
        getAddress(latLonPoint);
        /**
         * 设置地图加载完成回调
         */
        aMap.setOnMapLoadedListener(new AMap.OnMapLoadedListener() {
            @Override
            public void onMapLoaded() {
                /**
                 * 设置缩放级别为16
                 */
                aMap.moveCamera(CameraUpdateFactory.zoomTo(16));
                aMap.showMapText(true);
                aMap.showIndoorMap(false);

                //移动地图
                aMap.setOnCameraChangeListener(new AMap.OnCameraChangeListener() {
                    @Override
                    public void onCameraChange(CameraPosition cameraPosition) {

                    }

                    @Override
                    public void onCameraChangeFinish(CameraPosition cameraPosition) {
                        if(isFirst==false){
                            // 获取当前地图中心点的坐标
                            mTarget = aMap.getCameraPosition().target;
//                            currentlatLng=mTarget;
                            //准确位置获取
                            geocodeSearch = new GeocodeSearch(ChatActivity.this);
                            geocodeSearch.setOnGeocodeSearchListener(ChatActivity.this);
                            latLonPoint = new LatLonPoint(mTarget.latitude, mTarget.longitude);
                            getAddress(latLonPoint);
                            isFirst=true;
                            //第一次定位移动到定位点作为地图的中心点
                            if(isShowMoveLocationCenter==true){
                                aMap.setMyLocationType(AMap.LOCATION_TYPE_LOCATE);
                                isShowMoveLocationCenter=false;
                            }

                        }

                    }
                });

            }
        });
    }

    /**
     * 自定义定位图标
     */
    private void setMyLocationStyle() {
        MyLocationStyle myLocationStyle = new MyLocationStyle();// 自定义系统定位小蓝点
        myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.mipmap.o_map));// 设置小蓝点的图标
        myLocationStyle.strokeColor(Color.TRANSPARENT);// 设置圆形的边框颜色
        myLocationStyle.radiusFillColor(Color.TRANSPARENT);// 设置圆形的填充颜色
        myLocationStyle.anchor(0.5f, 0.5f);// 设置小蓝点的锚点
        myLocationStyle.strokeWidth(0);// 设置圆形的边框粗细
        aMap.setMyLocationStyle(myLocationStyle);
    }

    private void getLocation() {
        //经纬度获取
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        List<String> providers = locationManager.getProviders(true);
        if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
            locationProvider = LocationManager.NETWORK_PROVIDER;
        } else if (providers.contains(LocationManager.GPS_PROVIDER)) {//GPS的定位放在第一个  得不到数据
            locationProvider = LocationManager.GPS_PROVIDER;
        } else {
            return;
        }
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        location = locationManager.getLastKnownLocation(locationProvider);
        if (location != null) {
            ShowLocation(location);
            latLonPoint = new LatLonPoint(location.getLatitude(), location.getLongitude());
        } else {

        }
        locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);

    }

    LocationListener locationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            ShowLocation(location);
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onProviderDisabled(String provider) {

        }
    };


    private void ShowLocation(Location location) {
        latitude = location.getLatitude();
        longitude = location.getLongitude();
    }

    @Override
    public void activate(OnLocationChangedListener onLocationChangedListener) {
        mListener = onLocationChangedListener;
        if (mLocationClient == null) {
            mLocationClient = new AMapLocationClient(MBUtil.getContext());
            AMapLocationClientOption mLocationOption = new AMapLocationClientOption();
            mLocationClient.setLocationListener(this);
            mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
            mLocationClient.setLocationOption(mLocationOption);
            mLocationClient.startLocation();
        }
    }

    @Override
    public void deactivate() {
        mListener = null;
        if (mLocationClient != null) {
            mLocationClient.stopLocation();
            mLocationClient.onDestroy();
        }
        mLocationClient = null;
    }

    @Override
    public void onLocationChanged(AMapLocation aMapLocation) {
        if (mListener != null && aMapLocation != null) {
            if (aMapLocation.getErrorCode() == 0) {
                mListener.onLocationChanged(aMapLocation);// 显示系统蓝点

                latitude = aMapLocation.getLatitude();// 纬度
                longitude = aMapLocation.getLongitude();// 经度
                // TODO: 2016/6/2
            } else {
                String errText = "定位失败," + aMapLocation.getErrorCode() + ": " + aMapLocation.getErrorInfo();
                System.out.println(errText);
            }
        } else {


        }
    }

    @Override
    public void onRegeocodeSearched(RegeocodeResult result, int rCode) {

        if (rCode == 1000) {
            if (result != null && result.getRegeocodeAddress() != null && result.getRegeocodeAddress().getFormatAddress() != null) {

                String province = result.getRegeocodeAddress().getProvince();
                String district = result.getRegeocodeAddress().getDistrict();
                String township = result.getRegeocodeAddress().getTownship();
                String neighborhood = result.getRegeocodeAddress().getNeighborhood();

                //当前具体位置
                currentSpecificPosition = result.getRegeocodeAddress().getFormatAddress();

            } else {
                Toast.makeText(ChatActivity.this, "定位失败,请重试", Toast.LENGTH_SHORT);
            }
        } else if (rCode == 27) {
            Toast.makeText(ChatActivity.this, "定位失败,请重试", Toast.LENGTH_SHORT);
        } else if (rCode == 32) {
            Toast.makeText(ChatActivity.this, "定位失败,请重试", Toast.LENGTH_SHORT);
        } else {
            Toast.makeText(ChatActivity.this, "定位失败,请重试", Toast.LENGTH_SHORT);
        }

    }

    @Override
    public void onGeocodeSearched(GeocodeResult geocodeResult, int i) {

    }
    /**
     * 逆地理编码
     */
    public void getAddress(final LatLonPoint latLonPoint) {
        RegeocodeQuery regeocodeQuery = new RegeocodeQuery(latLonPoint, 200, GeocodeSearch.AMAP);
        geocodeSearch.getFromLocationAsyn(regeocodeQuery);
    }

    /***
     * scrollView截图
     *
     * @param scrollView
     * @return
     */
    public static Bitmap getBitmapByView(ScrollView scrollView) {
        int h = 0;
        Bitmap bitmap = null;

        for (int i = 0; i < scrollView.getChildCount(); i++) {
            h += scrollView.getChildAt(i).getHeight();
            scrollView.getChildAt(i).setBackgroundResource(R.mipmap.riding_report);
//            scrollView.getChildAt(i).setBackgroundColor(
//                    Color.parseColor("0xFFFFFFFF"));
        }

        bitmap = Bitmap.createBitmap(scrollView.getWidth(), h,
                Bitmap.Config.RGB_565);
        final Canvas canvas = new Canvas(bitmap);
        scrollView.draw(canvas);
        return bitmap;
    }

    /**
     * 保存到sdcard
     *
     * @param b
     * @return
     */
    public static String savePic(Bitmap b) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss",
                Locale.US);
        File outfile = new File("/sdcard/image");
        // 如果文件不存在,则创建一个新文件
        if (!outfile.isDirectory()) {
            try {
                outfile.mkdir();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        String fname = outfile + "/" + sdf.format(new Date()) + ".png";
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(fname);
            if (null != fos) {
                b.compress(Bitmap.CompressFormat.PNG, 90, fos);
                fos.flush();
                fos.close();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fname;
    }

    public static final int CHOOSE_IMAGE = 3;
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {


        if (requestCode == CameraConstants.REQUEST_TAKE && resultCode == Activity.RESULT_OK) {

            Log.d("ChatActivity", "imageUri:" + imageUri);
//            Picasso.with(ChatActivity.this).load(imageUri).into(img_bitmap);
//            Intent intent = new Intent(this, CropPhotoActivity.class);
//            intent.setData(imageUri);
//            intent.putExtra("exist", true);
//            startActivityForResult(intent, CHOOSE_IMAGE);
        }

        if (requestCode == CameraConstants.REQUEST_PICK && resultCode == Activity.RESULT_OK) {

//            Picasso.with(ChatActivity.this).load(data.getData()).into(img_bitmap);
            Log.d("ChatActivity", "data.getData():" + data.getData());

//            Uri uri = data.getData();
//            Intent intent = new Intent(this, CropPhotoActivity.class);
//            intent.setData(uri);
//            intent.putExtra("exist", true);
//            startActivityForResult(intent, CHOOSE_IMAGE);
        }

        //纯照片
//        if (requestCode == CHOOSE_IMAGE && resultCode == RESULT_OK) {
//            picStringUrl = data.getStringExtra("filePath");
//            Log.d("ChatActivity", picStringUrl);
//
//        }
        super.onActivityResult(requestCode, resultCode, data);
    }


}

闲暇之余把相册选取照片,拍摄也给大家提供了,另外我基于高德地图把位置的获取也做了(位置的照片,照片的路径,位置的描述及经纬度都获取了,大家可以自行借鉴,选取自己所需的)

(注:需要demo的可以评论留下邮箱或者私信我)

作者:madreain 发表于2016/8/14 17:11:05 原文链接
阅读:20 评论:0 查看评论

Java并发编程(七)ConcurrentLinkedQueue的实现原理和源码分析

$
0
0

相关文章
Java并发编程(一)线程定义、状态和属性
Java并发编程(二)同步
Java并发编程(三)volatile域
Java并发编程(四)Java内存模型
Java并发编程(五)ConcurrentHashMap的实现原理和源码分析
Java并发编程(六)阻塞队列

前言

我们要实现一个线程安全的队列有两种实现方式一种是使用阻塞算法,另一种是使用非阻塞算法。使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现,而非阻塞的实现方式则可以使用循环CAS的方式来实现,本节我们就来研究下ConcurrentLinkedQueue是如何保证线程安全的同时又能高效的操作的。

1.ConcurrentLinkedQueue的结构

ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。基于CAS的“wait-free”(常规无等待)来实现,CAS并不是一个算法,它是一个CPU直接支持的硬件指令,这也就在一定程度上决定了它的平台相关性。

当前常用的多线程同步机制可以分为下面三种类型:

  • volatile 变量:轻量级多线程同步机制,不会引起上下文切换和线程调度。仅提供内存可见性保证,不提供原子性。
  • CAS 原子指令:轻量级多线程同步机制,不会引起上下文切换和线程调度。它同时提供内存可见性和原子化更新保证。
  • 互斥锁:重量级多线程同步机制,可能会引起上下文切换和线程调度,它同时提供内存可见性和原子性。

ConcurrentLinkedQueue 的非阻塞算法实现主要可概括为下面几点:

  • 使用 CAS 原子指令来处理对数据的并发访问,这是非阻塞算法得以实现的基础。
  • head/tail 并非总是指向队列的头 / 尾节点,也就是说允许队列处于不一致状态。 这个特性把入队 /
    出队时,原本需要一起原子化执行的两个步骤分离开来,从而缩小了入队 /
    出队时需要原子化更新值的范围到唯一变量。这是非阻塞算法得以实现的关键。
  • 以批处理方式来更新head/tail,从整体上减少入队 / 出队操作的开销。


ConcurrentLinkedQueue由head节点和tail节点组成,每个节点(Node)由节点元素(item)和指向下一个节点的引用(next)组成,节点与节点之间就是通过这个next关联起来,从而组成一张链表结构的队列。默认情况下head节点存储的元素为空,tail节点等于head节点。

2.入队列

入队列就是将入队节点添加到队列的尾部,假设我们要在一个队列中依次插入4个节点,来看看下面的图来方便理解:

  • 添加元素1。队列更新head节点的next节点为元素1节点。又因为tail节点默认情况下等于head节点,所以它们的next节点都指向元素1节点。
  • 添加元素2。队列首先设置元素1节点的next节点为元素2节点,然后更新tail节点指向元素2节点。
  • 添加元素3,设置tail节点的next节点为元素3节点。
  • 添加元素4,设置元素3的next节点为元素4节点,然后将tail节点指向元素4节点。

入队主要做两件事情,第一是将入队节点设置成当前队列尾节点的下一个节点。第二是更新tail节点,在入队列前如果tail节点的next节点不为空,则将入队节点设置成tail节点,如果tail节点的next节点为空,则将入队节点设置成tail的next节点,所以tail节点不总是尾节点。

上面的分析从单线程入队的角度来理解入队过程,但是多个线程同时进行入队情况就变得更加复杂,因为可能会出现其他线程插队的情况。如果有一个线程正在入队,那么它必须先获取尾节点,然后设置尾节点的下一个节点为入队节点,但这时可能有另外一个线程插队了,那么队列的尾节点就会发生变化,这时当前线程要暂停入队操作,然后重新获取尾节点。让我们再通过源码来详细分析下它是如何使用CAS方式来入队的(JDK1.8):

    public boolean offer(E e) {
        checkNotNull(e);
        //创建入队节点
        final Node<E> newNode = new Node<E>(e);
        //t为tail节点,p为尾节点,默认相等,采用失败即重试的方式,直到入队成功
        for (Node<E> t = tail, p = t;;) {
            //获得p的下一个节点
            Node<E> q = p.next;
            // 如果下一个节点是null,也就是p节点就是尾节点
            if (q == null) {
              //将入队节点newNode设置为当前队列尾节点p的next节点
                if (p.casNext(null, newNode)) { 
                   //判断tail节点是不是尾节点,也可以理解为如果插入结点后tail节点和p节点距离达到两个结点
                    if (p != t) 
                     //如果tail不是尾节点则将入队节点设置为tail。
                     // 如果失败了,那么说明有其他线程已经把tail移动过 
                        casTail(t, newNode);  
                    return true;
                }
            }
                 // 如果p节点等于p的next节点,则说明p节点和q节点都为空,表示队列刚初始化,所以返回                            head节点
            else if (p == q)
                p = (t != (t = tail)) ? t : head;
            else
                //p有next节点,表示p的next节点是尾节点,则需要重新更新p后将它指向next节点
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }

从源代码我们看出入队过程中主要做了三件事情,第一是定位出尾节点;第二个是使用CAS指令将入队节点设置成尾节点的next节点,如果不成功则重试;第三是重新定位tail节点。
从第一个if判断就来判定p有没有next节点如果没有则p是尾节点则将入队节点设置为p的next节点,同时如果tail节点不是尾节点则将入队节点设置为tail节点。如果p有next节点则p的next节点是尾节点,需要重新更新p后将它指向next节点。还有一种情况p等于p的next节点说明p节点和p的next节点都为空,表示这个队列刚初始化,正准备添加数据,所以需要返回head节点。

3.出队列

出队列的就是从队列里返回一个节点元素,并清空该节点对元素的引用。让我们通过每个节点出队的快照来观察下head节点的变化。

从上图可知,并不是每次出队时都更新head节点,当head节点里有元素时,直接弹出head节点里的元素,而不会更新head节点。只有当head节点里没有元素时,出队操作才会更新head节点。让我们再通过源码来深入分析下出队过程(JDK1.8):

    public E poll() {
        // 设置起始点  
        restartFromHead:
        for (;;) {
        //p表示head结点,需要出队的节点
            for (Node<E> h = head, p = h, q;;) {
            //获取p节点的元素
                E item = p.item;
                //如果p节点的元素不为空,使用CAS设置p节点引用的元素为null
                if (item != null && p.casItem(item, null)) {

                    if (p != h) // hop two nodes at a time
                    //如果p节点不是head节点则更新head节点,也可以理解为删除该结点后检查head是否与头结点相差两个结点,如果是则更新head节点
                        updateHead(h, ((q = p.next) != null) ? q : p);
                    return item;
                }
                //如果p节点的下一个节点为null,则说明这个队列为空,更新head结点
                else if ((q = p.next) == null) {
                    updateHead(h, p);
                    return null;
                }
                //结点出队失败,重新跳到restartFromHead来进行出队
                else if (p == q)
                    continue restartFromHead;
                else
                    p = q;
            }
        }
    }

更新head节点的updateHead方法:

final void updateHead(Node<E> h, Node<E> p) 
{
     // 如果两个结点不相同,尝试用CAS指令原子更新head指向新头节点
     if (h != p && casHead(h, p))
         //将旧的头结点指向自身以实现删除
     h.lazySetNext(h);
}

首先获取head节点的元素,并判断head节点元素是否为空,如果为空,表示另外一个线程已经进行了一次出队操作将该节点的元素取走,如果不为空,则使用CAS的方式将head节点的引用设置成null,如果CAS成功,则直接返回head节点的元素,如果CAS不成功,表示另外一个线程已经进行了一次出队操作更新了head节点,导致元素发生了变化,需要重新获取head节点。如果p节点的下一个节点为null,则说明这个队列为空(此时队列没有元素,只有一个伪结点p),则更新head节点。

4.队列判空

有些人在判断队列是否为空时喜欢用queue.size()==0,让我们来看看size方法:

public int size() 
{
     int count = 0;
     for (Node<E> p = first(); p != null; p = succ(p))
         if (p.item != null)
             // Collection.size() spec says to max out
             if (++count == Integer.MAX_VALUE)
                 break;
     return count;
 }

可以看到这样在队列在结点较多时会依次遍历所有结点,这样的性能会有较大影响,因而可以考虑empty函数,它只要判断第一个结点(注意不一定是head指向的结点)。

  public boolean isEmpty() {
        return first() == null;
    }

参考资料
《Java并发编程的艺术》
JDK1.8源码
http://www.zsfblues.com/2016/06/15/ConcurrentLinkedQueue%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/

作者:itachi85 发表于2016/8/14 17:21:24 原文链接
阅读:33 评论:0 查看评论

java/android 设计模式学习笔记(16)---命令模式

$
0
0

   这篇博客我们来介绍一下命令模式(Command Pattern),它是行为型设计模式之一。命令模式相对于其他的设计模式更为灵活多变,我们接触比较多的命令模式个例无非就是程序菜单命令,如在操作系统中,我们点击关机命令,系统就会执行一系列的操作,如先是暂停处理事件,保存系统的一些配置,然后结束程序进程,最后调用内核命令关闭计算机等,对于这一系列的命令,用户不用去管,用户只需点击系统的关机按钮即可完成如上一系列的命令。而我们的命令模式其实也与之相同,将一系列的方法调用封装,用户只需调用一个方法执行,那么所有的这些被封装的方法就会被挨个执行调用。
  转载请注明出处:http://blog.csdn.net/self_study/article/details/52091539
  PS:对技术感兴趣的同鞋加群544645972一起交流。

设计模式总目录

  java/android 设计模式学习笔记目录

特点

  将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。
  命令模式的使用场景:

  • 需要抽象出待执行的动作,然后以参数的形式提供出来—类似于过程设计中的回调机制,而命令模式正是回调机制的一个面向对象的替代品;
  • 在不同的时刻指定、排列和执行请求,一个命令对象可以有与初始请求无关的生存期;
  • 需要支持取消操作;
  • 支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一遍;
  • 需要支持事务操作;
  • 系统需要将一组操作组合在一起,即支持宏命令。
  wiki 上列出的具体使用场景:
  • GUI buttons and menu items
  • 在 Java Swing 和 Delphi 语言中,一个 Action 是一个命令,除了执行预定的命令之外,一个 Action 可能会有一个相关联的图标,键盘快捷键,气泡提示文本等等。一个工具栏按钮或者菜单的元素可能完全用一个 Action 对象进行初始化。
  • Macro recording
  • 如果所有的用户动作都代表了一个个命令对象,那么一个程序就能够轻易的保存一系列的命令对象,之后能够将这一系列的命令重新执行一遍来实现一个回播的动作。如果程序中嵌入了脚本引擎,那么每个命令对象都能够实现 toScript() 方法,用户的动作也能够被轻松的保存为脚本对象。
  • Mobile Code
  • 类似于 Java 这种能够通过 URLClassloders 将代码变成流从一个地方传输到另一个地方的语言,并且代码库中的命令使新的行为能够被传递到远程位置(EJB Command,Master Worker模式)。
  • Multi-level undo
  • 如果程序中所有的用户动作都被实现成了命令对象,程序就能够保存最近被执行的命令对象,当用户想要撤销某些命令时,程序就可以简单的 pop 出最近的命令并且执行他的 undo 方法。
  • Networking
  • 能够将所有的命令对象通过网络传输到另外一台设备上去执行,比如端游中的用户操作等。
  • Parallel Processing
  • 所有的命令都被写成了共享资源中的任务并且并发的被很多线程同时执行(在远程机器上这种变体可能这个会被称为 Master/Worker 模式)。
  • Progress bars
  • 我们假定程序有一系列需要按顺序执行的命令,如果每个命令对象都有一个 getEstimatedDuration() 方法,那么程序就可以轻松估算出整体的执行时间,然后展示一个有意义的进度条来反应当前所有任务的执行程度。
  • Thread pools
  • 一个具有代表性的线程池可能会有一个 public 的 addTask() 方法用来添加一个工作任务到内部的等待队列中,这个线程池中会有一系列的线程用来执行队列中的一系列命令对象。普遍的,这些对象会实现一个通用的接口,比如 Runnable 等,用来允许线程池执行这些命令,虽然这个线程池类并不了解它会被用来处理具体的什么任务。
  • Transactional behavior
  • 类似于 undo 操作,一个数据库引擎或者软件安装器可能会维护一个已经执行或者将要执行的操作列表,如果其中的一个失败了,所有其他的都会被还原或者抛弃(通常被称为 rollback,回滚)。举个例子,如果相关联的两个数据库表必须要更新,并且第二个更新失败,这个事务将会被回滚,所以第一个表的更新也会被抛弃。
  • Wizards
  • 向导页是用户在点击最后一页”结束”按钮时候弹出来的几页配置页(配置用户的使用习惯等),在这种情况下,一个通常分离用户操作代码和程序代码的方法就是使用命令对象实现向导功能。这个命令对象会在向导页第一次展示的时候被创建,每个向导页将它们自己的 GUI 变化保存在一个命令对象中,所以这个对象被定位为用户的进一步操作。“结束”动作简单的触发了一个 excete() 动作,这样一来,这个命令类将会达到预期的效果。

UML类图

  我们来看看命令模式的 uml 类图:
  这里写图片描述
命令模式的角色介绍:

  • Receiver:接收者角色
  • 该类负责具体实施或执行一个请求,说的通俗一点就是,执行具体逻辑的角色,以上面说到的“关机”操作命令为例,其接收者角色就是真正执行各项关机逻辑的底层代码。任何一个类都能成为一个接收者,而接收者类中封装具体操作逻辑的方法我们则称为行动方法;
  • Command :命令接口
  • 定义所有具体命令类基本行为的抽象接口;
  • ConreteCommand:具体命令角色
  • 该类实现了 Command 接口,在 execute 方法中调用接收者角色的相关方法,在接收者和命令执行的具体行为之间加以弱耦合。而 execute 则通常称为执行方法,如上面提到的“关机”操作实现,具体可能还包含很多相关的操作,比如保存数据、关闭文件、结束进程等,如果将这一些列的具体逻辑处理看作接收者,那么调用这些具体逻辑的方法就可以看作是执行方法;
  • Invoker :请求者角色
  • 该类的职责就是调用命令对象执行具体的请求,相关的方法我们称为行动方法,“关机”命令为例,关机这个命令一般就对应着一个关机方法,执行关机命令就相当于由这个关机方法去执行具体的逻辑,这个关机方法就可以看作是请求者;
  • Client:客户端角色
  由此我们可以写出命令模式的通用代码:
Receiver.class 具体逻辑执行者

public class Receiver {
    public void action() {
        System.out.print("执行具体的操作");
    }
}

Command.class 抽象命令类

public interface Command {
    void execute();
}

ConcreteCommand.clas 具体命令类

public class ConcreteCommand implements Command {

    private Receiver receiver;

    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.action();
    }
}

Invoker.class 请求者

public class Invoker {

    private Command command;

    public Invoker(Command command) {
        this.command = command;
    }

    public void action() {
        command.execute();
    }
}

Client 客户端

public class Client {
    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        Command command = new ConcreteCommand(receiver);
        Invoker invoker = new Invoker(command);
        invoker.action();
    }
}

最后也能通过 Invoker 类调用到真正的 Receiver 执行逻辑了。

示例与源码

  这就以一个简单的控制电灯亮灭和门开关的情景为例:
Light.class 和 Door.class 实际控制类

public class Light {
    public void lightOn() {
        System.out.print("light on\n");
    }

    public void lightOff() {
        System.out.print("light off\n");
    }
}
public class Door {
    public void doorOpen() {
        System.out.print("door open\n");
    }

    public void doorClose() {
        System.out.print("door close\n");
    }
}

然后是电灯的控制类:
LightOnCommand.class 和 LightOffCommand.class

public class LightOnCommand  implements Command{
    public Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.lightOn();
    }
}
public class LightOffCommand implements Command{
    public Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.lightOff();
    }
}

门的相关控制类:
DoorOpenCommand.class 和 DoorCloseCommand.class

public class DoorOpenCommand implements Command{

    public Door door;

    public DoorOpenCommand(Door door) {
        this.door = door;
    }

    @Override
    public void execute() {
        door.doorOpen();
    }
}
public class DoorCloseCommand implements Command{

    public Door door;

    public DoorCloseCommand(Door door) {
        this.door = door;
    }

    @Override
    public void execute() {
        door.doorClose();
    }
}

然后是一个无操作默认命令类:
NoCommand.class

public class NoCommand implements Command{
    @Override
    public void execute() {
    }
}

最后是控制类:
Controller.class

public class Controller {
    private Command[] onCommands;
    private Command[] offCommands;

    public Controller() {
        onCommands = new Command[2];
        offCommands = new Command[2];

        Command noCommand = new NoCommand();
        onCommands[0] = noCommand;
        onCommands[1] = noCommand;
        offCommands[0] = noCommand;
        offCommands[1] = noCommand;
    }

    public void setCommand(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }

    public void onCommand(int slot) {
        onCommands[slot].execute();
    }

    public void offCommand(int slot) {
        offCommands[slot].execute();
    }
}

测试代码

Light light = new Light();
Door door = new Door();

LightOnCommand lightOnCommand = new LightOnCommand(light);
LightOffCommand lightOffCommand = new LightOffCommand(light);

DoorOpenCommand doorOpenCommand = new DoorOpenCommand(door);
DoorCloseCommand doorCloseCommand = new DoorCloseCommand(door);

Controller controller = new Controller();
controller.setCommand(0, lightOnCommand, lightOffCommand);
controller.setCommand(1, doorOpenCommand, doorCloseCommand);

controller.onCommand(0);
controller.offCommand(0);
controller.onCommand(1);
controller.offCommand(1);

结果:
这里写图片描述
这样就实现了对 Light 和 Door 的控制了,其实这个例子只是实现了对命令模式的基本框架而已,命令模式的用处其实在于它的日志和回滚撤销功能,每次执行命令的时候都打印出相应的关键日志,或者每次执行后都将这个命令保存进列表中并且每个命令实现一个 undo 方法,以便可以进行回滚。另外,也可以构造一个 MacroCommand 宏命令类用来按次序先后执行几条相关联命令。当然可以发散的空间很多很多,感兴趣的可以自己去实现,原理都是一样的。

总结

  命令模式将发出请求的对象和执行请求的对象解耦,被解耦的两者之间通过命令对象进行沟通,调用者通过命令对象的 execute 放出请求,这会使得接收者的动作被调用,调用者可以接受命令当作参数,甚至在运行时动态地进行,命令可以支持撤销,做法是实现一个 undo 方法来回到 execute 被执行前的状态。MacroCommand 宏命令类是命令的一种简单的延伸,允许调用多个命令,宏方法也可以支持撤销。日志系统和事务系统可以用命令模式来实现。
  命令模式的优点很明显,调用者和执行者之间的解耦,更灵活的控制性,以及更好的扩展性等。缺点更明显,就是类的爆炸,大量衍生类的创建,这也是大部分设计模式的“通病”,是一个没有办法避免的问题。

源码下载

  https://github.com/zhaozepeng/Design-Patterns/tree/master/CommandPattern

引用

https://en.wikipedia.org/wiki/Command_pattern
http://blog.csdn.net/jason0539/article/details/45110355

作者:zhao_zepeng 发表于2016/8/14 18:30:19 原文链接
阅读:4 评论:0 查看评论
Viewing all 5930 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>