android菜鳥練手小項目之自定義日曆,涵蓋LitePal數據庫,極光推送,聊天機器人(四)

   六月在忙公司的項目和自己的一些事情,差點這個小系列又要夭折了,還是抽空把它寫完,希望能給新手和菜鳥一些啓示,下面看下最終實現的效果。



作爲最後的一個部分,想跟大家分享下這個聊天機器人的實現,這部分呢也是參考網上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



-----------------------結束語-----------------------


沒有誰生來就是強大的,什麼都懂,做技術唯有不斷的練習和思考纔會熟能生巧

就像賣油翁說的:無他,唯手熟爾!

祝願每一個菜鳥(包括我)都能成爲大神







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章