android菜鸟练手小项目之自定义日历,涵盖LitePal数据库,极光推送,聊天机器人(四)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010898329/article/details/73864920

   六月在忙公司的项目和自己的一些事情,差点这个小系列又要夭折了,还是抽空把它写完,希望能给新手和菜鸟一些启示,下面看下最终实现的效果。



作为最后的一个部分,想跟大家分享下这个聊天机器人的实现,这部分呢也是参考网上hyman(鸿洋大神)视频来实现的。下面是链接地址《Android智能机器人“小慕”的实现-慕课网》 http://www.imooc.com/learn/217


如果想要实现聊天机器人的功能,需要去图灵官网注册号,http://www.tuling123.com/

下面直接撸代码,讲解下我认为值得新手借鉴学习的部分。

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main , menu); // 加载菜单XML
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()){
        case R.id.menu_chat:
            startActivity(new Intent(MainActivity.this , ChatActivity.class));
            return true ;
        default:
            return super.onOptionsItemSelected(item);
    }
}
这是点击ActionBar右侧的机器人图标的事件,其实就是加载一个菜单布局,很多新手可能没用过这个,大家要用的话可以借鉴。

对应的R.menu.main  , 注意该xml文件位于res/menu/main.xml中,所以可以直接调用R.menu.main加载进来,当然你可以添加更多的item。

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

    <item android:id="@+id/menu_chat"
        app:showAsAction="always"
        android:title="@string/chat"
        android:icon="@drawable/icon"/>

</menu>

点击之后进入了聊天界面,就是上面看到的那个界面,主要是一个ListView作为容器,根据itmType的类型不同加载不同的item,也就是看到的左右布局。

public class ChatActivity extends AppCompatActivity {

    private TextView titleTv ;
    private ImageView backImg ;

    private ListView mMsgListView ;
    private EditText mMsgEditTv ;
    private Button mSendBtn ;

    private List<ChatMessage> mMsgDatas ;
    private ChatMsgAdapter mMsgAdapter ;

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            ChatMessage chat = (ChatMessage) msg.obj;
            mMsgDatas.add(chat);
            mMsgAdapter.notifyDataChanged(mMsgDatas); // 聊天列表数据刷新
            mMsgListView.setSelection(mMsgDatas.size()-1);
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);
        getSupportActionBar().hide();

        initView();
        initDatas();
        initSendListener(); // 监听发送按钮事件
    }

    private void initSendListener(){
        mSendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final  String sendMsg = mMsgEditTv.getText().toString();
                if(TextUtils.isEmpty(sendMsg)){
                    Toast.makeText(ChatActivity.this,"输入消息不能为空!",Toast.LENGTH_SHORT).show();
                }

                ChatMessage chatMsg = new ChatMessage();
                chatMsg.setDate(new Date());
                chatMsg.setMsg(sendMsg);
                chatMsg.setMsgType(ChatMessage.MsgType.SEND_MSG);
                mMsgDatas.add(chatMsg);
                mMsgAdapter.notifyDataChanged(mMsgDatas); // 数据集合刷新
                mMsgListView.setSelection(mMsgDatas.size()-1); // 选中最后的一项
                mMsgEditTv.setText("");

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        ChatMessage receivedMsg = HttpUtil.sendMessage(sendMsg);
                        Message m = Message.obtain();
                        m.obj = receivedMsg;
                        mHandler.sendMessage(m);
                    }
                }).start();

            }
        });
    }

    private void initView() {
        titleTv = (TextView) this.findViewById(R.id.chat_title_tv);
        titleTv.setText("与小琳聊天中……");

        backImg = (ImageView) this.findViewById(R.id.chat_back_img);
        mMsgListView = (ListView) this.findViewById(R.id.content_msg_listView);
        mMsgEditTv = (EditText) this.findViewById(R.id.edit_msg_text);
        mSendBtn = (Button) this.findViewById(R.id.send_button);

        backImg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ChatActivity.this.finish();
            }
        });
    }

    private void initDatas(){
        // 初始化数据集合
        mMsgDatas = new ArrayList<>();
        // 初始化适配器
        mMsgAdapter = new ChatMsgAdapter();
        // 直接给列表设置适配器
        mMsgListView.setAdapter(mMsgAdapter);

        // 添加一条新数据
        mMsgDatas.add(new ChatMessage("你好,小琳为你服务!", ChatMessage.MsgType.RECEIVE_MSG,new Date()));
        // 然后调用数据刷新的方法刷新数据集合
        mMsgAdapter.notifyDataChanged(mMsgDatas); // 第一次刷新数据
    }
}

这个也没什么新奇的地方,看下适配器Adapter的写法。

public class ChatMsgAdapter extends BaseAdapter {

    private List<ChatMessage> chatMassageList  = new ArrayList<>();

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

    @Override
    public ChatMessage getItem(int position) {
        return chatMassageList.get(position);
    }

    // 获取当前条目的属性值
    @Override
    public int getItemViewType(int position) {
        // 通过当前的条目的类型,返回0(我的消息)或者1(对方的消息),
        return  getItem(position).getMsgType() == ChatMessage.MsgType.RECEIVE_MSG ? 0 : 1 ;
    }

    @Override
    public int getViewTypeCount() {
        return 2; // 只有两种属性情况
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null ;
        // 通过父容器(parent)来获取上下问(Context        LayoutInflater mInflater = LayoutInflater.from(parent.getContext());
        if (convertView == null){
            // 根据类型来加载左右不同的布局界面
            if(getItemViewType(position) == 0){
                convertView = mInflater.inflate(R.layout.item_from_msg,null);
                holder = new ViewHolder();
                holder.mDateTv = (TextView) convertView.findViewById(R.id.id_form_msg_date);
                holder.mMsgTv = (TextView) convertView.findViewById(R.id.id_from_msg_info);
            }else{
                holder = new ViewHolder();
                convertView = mInflater.inflate(R.layout.item_to_msg,null);
                holder.mDateTv = (TextView) convertView.findViewById(R.id.id_to_msg_date);
                holder.mMsgTv = (TextView) convertView.findViewById(R.id.id_to_msg_info);
            }
            convertView.setTag(holder);
        }else{
            holder = (ViewHolder) convertView.getTag();
        }

        // 设置数据
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd hh:mm:ss");
        holder.mDateTv.setText(sdf.format(new Date()));
        holder.mMsgTv.setText(getItem(position).getMsg());
        return convertView;

    }

    private final class ViewHolder {
        TextView mDateTv ;
        TextView mMsgTv ;
    }

    /**
     * 提供给外部调用,数据集合改变并且刷新列表的方法
     * @param chatMassageList
     */
    public void notifyDataChanged(List<ChatMessage> chatMassageList){
        this.chatMassageList = chatMassageList ;
        this.notifyDataSetChanged();
    }

}

这个我要叨逼叨逼

传统的Adapter写法如下

public class DemoAdapter extends BaseAdapter {
    
    private Context context ;
    
    private List<ChatMessage> chatList ;

    public DemoAdapter() {
    }

    public DemoAdapter(Context context, List<ChatMessage> chatList) {
        this.context = context;
        this.chatList = chatList;
    }

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

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        
        // 使用ViewHolder进行布局加载和渲染
        
        
        return null;
    }
}

1。对比一下发现,上面那种写法并没有传入Context,而是直接通过getView中的parent.getContext()获取的上下文,这种写法看起来更美观,少了一个参数,而且从parent中获取的Context不会出错。

2。如果用下面一种Adapter写法,在给ListView设置了Adapter之后,我们常常可能会忘了要去调用一下notifyDataSetChanged方法,导致有时候列表刷新出现错误,换成第一种写法之后,数据集合在要使用的地方(Activity或Fragment中初始化一次之后),我们先给Adapter设置数据,然后将该Apapter设置给ListView,等添加了数据之后我们在直接调用自己定义的notifyData方法就可以了,感觉思路更清晰,不会忘。每次数据集合发生变化的时候就记得要通知刷新列表。


3。另外比较值得借鉴的地方是Adapter两个重写的方法



这两个方法可以设置多个属性,然后根据当前加载item的属性去做各种显示处理,比如我这里是根据属性的不同设置加载左布局还是右布局,当初为了这个的实现,我硬生生的去琢磨了一个方法,结果效果并不好,现在回过头来看,还是这个比较简单实用。


这个是我原来的方式实现的左右布局,《竖直时间轴之左右交叉布局》

http://blog.csdn.net/u010898329/article/details/51763195

为了实现上述类似的功能,写了好几十行代码做判断,结果还是有BUG,截个图大家看下





要是早知道这个方法,就不用这么费神了,所以我觉得新手或者菜鸟都可以借鉴下!


源码下载  http://download.csdn.net/detail/u010898329/9883518



-----------------------结束语-----------------------


没有谁生来就是强大的,什么都懂,做技术唯有不断的练习和思考才会熟能生巧

就像卖油翁说的:无他,唯手熟尔!

祝愿每一个菜鸟(包括我)都能成为大神







猜你喜欢

转载自blog.csdn.net/u010898329/article/details/73864920
今日推荐