空暇之余。想到上次看过的一个图灵机器人开发视频。直接上其官网看开发人员手冊,自己动手写了一个图灵机器人小应用。编写的思路基本和网上的一些开发视频不谋而合,都是网络訪问+json解析,再者就是设计出好看的界面。有兴趣的同学能够依据自己的需求进行更改甚至细化里面的查询功能,全然能够做出一款完整的app应用。

先上图看看我的demo效果:


android 开发平台 机器人 机器人安卓平板开发_android

  

android 开发平台 机器人 机器人安卓平板开发_android 开发平台 机器人_02

 


这个项目主要包含三个部分:UI设计与开发、调用图灵API获取数据json解析封装成Bean、listview的优化显示。


UI设计与开发

先看下整个xml的UI布局

android 开发平台 机器人 机器人安卓平板开发_ide_03


布局非常简单,从左上角能够看到整个布局的排版。这对于android开发人员来说非常easy,当中listview使用了下面设置是为了填满中间空间。



android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"


整个UI设计主要考虑的重点在于聊天边框的实现以及shape和selector的使用。


使用圆角的边缘shape文件(拿EditText为例)



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

    <gradient
        android:endColor="#ffffff"
        android:startColor="#ffffff" />

    <corners
        android:bottomLeftRadius="5dp"
        android:bottomRightRadius="5dp"
        android:topLeftRadius="5dp"
        android:topRightRadius="5dp" />

    <stroke
        android:width="1dip"
        android:color="#ffffff" />

</shape>


使用圆角并实现selector文件(拿Button为例)



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

    <item android:state_focused="true"
        android:state_pressed="true"
        android:drawable="@drawable/start_clickafter" />

    <item android:state_focused="false"
        android:state_pressed="true"
        android:drawable="@drawable/start_clickafter" />

    <item
        android:state_selected="true"
        android:drawable="@drawable/start_clickafter"
        />

    <item
        android:state_focused="true"
        android:drawable="@drawable/start_clickafter"
        />

    <!-- 默认时的背景图片-->

    <item android:state_focused="false" android:drawable="@drawable/start_clickbefore" />

</selector>


对于聊天框的配置,我们须要实现左边和右边聊天框效果(例如以下图所看到的)


android 开发平台 机器人 机器人安卓平板开发_android 开发平台 机器人_04


我们须要配置左右两个xml布局。

左布局例如以下:



<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/id_time"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="10sp"
        android:layout_alignParentTop="true"
        android:gravity="center"/>

    <com.example.yummylau.turingrobot.ui.CircularImage
        android:layout_below="@+id/id_time"
        android:id="@+id/id_iv"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:padding="10dp"
        />

    <TextView
        android:layout_below="@+id/id_time"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="50dp"
        android:layout_toRightOf="@+id/id_iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/id_tv"
        android:gravity="center_vertical"
        android:textSize="17sp"
        android:background="@drawable/aio_friend_bg_nor_11"
        />

</RelativeLayout>


右布局例如以下:



<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/id_time"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="10sp"
        android:layout_alignParentTop="true"
        android:gravity="center"/>

    <com.example.yummylau.turingrobot.ui.CircularImage
        android:layout_below="@+id/id_time"
        android:layout_alignParentRight="true"
        android:id="@+id/id_iv"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:padding="10dp"
        />

    <TextView
        android:layout_below="@+id/id_time"
        android:layout_marginTop="10dp"
        android:layout_marginRight="5dp"
        android:layout_marginLeft="50dp"
        android:layout_toLeftOf="@+id/id_iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/id_tv"
        android:gravity="center_vertical"
        android:textSize="17sp"
        android:background="@drawable/aio_user_bg_nor_11"
        />

</RelativeLayout>


当中com.example.yummylau.turingrobot.ui.CircularImage是我自己实现的一个圆形头像。有兴趣的同学能够在最下方下载源代码进行參考。而事实上这些布局并不难。最基本的是处理好“聊天气泡”的制作。

本人是使用android studio自带工具(强烈推荐)


android 开发平台 机器人 机器人安卓平板开发_android_05



上述时我在实现聊天气泡时用.9.png工具来实现。假设不是android studio IDE的能够自己的Android SDK\tools文件夹下双击draw9.path.bat来启动这个工具。

(1)上和左各划了1dp的长度。长度是用来拉伸的。假设你图片内内容太宽或者太高,则会依照你划的高度的内容来等量延伸。举个样例,比方说你的内容在竖直方向多出了300dp,那么图片就在话的左边的点(我们是1dp)自己主动添加300x1dp的长度来适配你的内容。

(2)右和下一般画得比較长。上述图片中看到两个矩形相重叠,重叠部分就是我们用户用来存放内容的。

点击“Show content”能够看到右側有内容填充的演示效果。

学会了.9.png的处理。再也不用操心图片变形了。而整个应用的UI设计和开发大致也就这么多。


调用图灵API获取数据json解析封装成Bean

打开图灵机器人官网 http://www.tuling123.com/openapi/,先注冊自己的账号,之后进如个人中心。找到自己的API KEY,然后保存在自己本地。

而个人中心里面还有非常多功能:机器人设定、机器人调教等等。能够让自己的图灵机器人自主学习,回答指定问题等等。有兴趣的朋友能够继续研究。

调用官方的API十分简单。仅仅须要用get方法,把key和须要发送的消息(String)整合到url然后进行訪问。取得系统返回的string再进行json解析就ok了。仅仅须要下面几个步骤


(1)重写自己的网络訪问类(依据自己的爱好),代码例如以下:


package com.example.yummy.turingrobot;

import android.os.AsyncTask;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * 异步网络訪问类
 * Created by yummy on 2015/7/12.
 */
public class MyAsync extends AsyncTask<String,Void,String>{


    private String url;
    private HttpClient httpClient;
    private HttpGet httpGet;
    private HttpResponse httpResponse;
    private HttpEntity httpEntity;
    private InputStream inputStream;

    //使用回调的方法
    private CallData data;



    public MyAsync(String url, CallData data) {
        this.url = url;
        this.data = data;
    }

    /**
     * 实现后台网络訪问获取数据
     * @param params
     * @return
     */
    @Override
    protected String doInBackground(String... params) {

        try{
            httpClient = new DefaultHttpClient();
            httpGet = new HttpGet(url);
            httpResponse = httpClient.execute(httpGet);
            httpEntity = httpResponse.getEntity();
            httpResponse.getStatusLine();
            inputStream = httpEntity.getContent();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line = null;
            StringBuffer stringBuffer = new StringBuffer();
            while ((line = bufferedReader.readLine())!=null){
                stringBuffer.append(line);
            }

            return stringBuffer.toString();
        }catch(Exception e){
            return new String("亲,訪问出错哦!");
        }
    }

    @Override
    protected void onPostExecute(String s) {
        data.getDataUrl(s);

    }
}


(2)写一个数据接口,使得AsyncTask可以回调MainActivity组件进行显示。(一般也可以通过在AsyncTask的构造器中传进MainActivity引用来调用其组件)


package com.example.yummy.turingrobot;

/**
 * 数据回调接口
 * Created by yummy on 2015/7/12.
 */
public interface CallData {
    void getDataUrl(String data);
}



@Override
         public void getDataUrl(String data) {
        System.out.println(data);
        lists.add(dataParse(data));
        //通知改变
        adapter.notifyDataSetChanged();
    }


MyAsync myAsync = (MyAsync)new MyAsync("http://www.tuling123.com/openapi/api?" +
                    "key=<自己的key>"+"发送内容",this).execute();



/**
     * 解析数据
     * @param result
     * @return
     */
    public Bean dataParse(String result){

        try{
            if(result.equals("亲。你还没有联网哦!")){
                Bean listData = new Bean(result, Bean.RECEIVER,getTime());
                return listData;
            }else{
                JSONObject jsonObject = new JSONObject(result);
                Bean listData = new Bean(jsonObject.getString("text"), Bean.RECEIVER,getTime());
                return listData;
            }
        }catch (Exception e){
            return null;
        }
    }


listview的优化显示

因为左右两边布局。传统的listview优化方法须要进行改进。

一般我们使用viewholder来进行缓存,只是因为多布局适配。所以应该相应多布局缓存。

策略:使用多个viewholder进行缓存,而且依据不同布局给予不同标志,然后在getView方法中进行推断。详细代码例如以下:


package com.example.yummy.turingrobot;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.example.yummylau.turingrobot.ui.CircularImage;

import java.util.List;

/**
 * 适配器
 * Created by yummy on 2015/7/12.
 */
public class Adapter extends BaseAdapter{

    private List<Bean> lists;
    private Context context;
    private LayoutInflater inflater;
    private ViewHolderLeft left;
    private ViewHolderRight right;

    //布局数目
    private final int VIEW_TYPE = 2;
    //0为发送
    private final int TYPE_0 = 0;
    //1为接受
    private final int TYPE_1 = 1;



    public Adapter(List<Bean> lists,Context context) {
        this.lists = lists;
        this.context = context;
    }

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

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

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


    /**
     * 说明下这种方法。每个view被调用的时候都会调用此方法。获取当前所须要的view样式
     * @param position
     * @return
     */
    @Override
    public int getItemViewType(int position) {
        return (lists.get(position).getFlag()== Bean.SEND)?TYPE_0:TYPE_1;
    }


    /**
     * 返回布局类型
     * @return
     */
    @Override
    public int getViewTypeCount() {
        return VIEW_TYPE;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        left = null;
        right = null;

        inflater = LayoutInflater.from(context);

        int tpye = getItemViewType(position);
       //System.out.print(tpye+"                             ddd            ");

        //初始化布局
        if(convertView == null){

            switch (tpye){

                //0为发送
                case TYPE_0:
                    convertView = inflater.inflate(R.layout.rightitem,null);
                    right = new ViewHolderRight();
                    right.rtextView = (TextView)convertView.findViewById(R.id.id_tv);
                    right.rtime = (TextView)convertView.findViewById(R.id.id_time);
                    right.riv = (CircularImage)convertView.findViewById(R.id.id_iv);
                    convertView.setTag(right);
                    break;

                //1为接受
                case TYPE_1:
                    convertView = inflater.inflate(R.layout.leftitem,null);
                    left = new ViewHolderLeft();
                    left.ltextView = (TextView)convertView.findViewById(R.id.id_tv);
                    left.ltime = (TextView)convertView.findViewById(R.id.id_time);
                    left.liv = (CircularImage)convertView.findViewById(R.id.id_iv);
                    convertView.setTag(left);
                    break;
            }

        }else{

            switch (tpye){

                //0为发送
                case TYPE_0:
                    right = (ViewHolderRight)convertView.getTag();
                    break;

                //1为接收
                case TYPE_1:
                    left = (ViewHolderLeft)convertView.getTag();
                    break;

            }

        }


        /**
         * 设置每个布局的资源
         */
        switch (tpye){

            //0为发送
            case TYPE_0:
                right.rtextView.setText(lists.get(position).getContent());
                right.rtime.setText(lists.get(position).getTime());
                right.riv.setImageResource(R.drawable.youtouxiang);
                break;

                //1为接收
            case TYPE_1:
                left.ltextView.setText(lists.get(position).getContent());
                left.ltime.setText(lists.get(position).getTime());
                left.liv.setImageResource(R.drawable.zuotouxiang);
                break;

        }


        return convertView;

    }


    //左側缓冲类
    static class ViewHolderLeft{
        TextView ltextView;
        TextView ltime;
        CircularImage liv;
    }

    //右側缓冲类
    static class ViewHolderRight{
        TextView rtextView;
        TextView rtime;
        CircularImage riv;
    }


}


getViewTypeCount方法是返回我们布局的个数;

getItemViewType方法是依据数据的position来推断我们须要选哪个布局。所以这种方法的逻辑须要自己来实现。


其它的操作基本和listview的优化操作是一样。

先推断convertView是否为空,空则初始化holder,不同则拿到相应标志的holder,最后适配数据进去就好了。


到从,整个图灵机器人的开发基本就结束了。


CSDN项目下载链接:图灵机器人下载链接

github项目链接:图灵机器人github链接