zoukankan      html  css  js  c++  java
  • 打造高仿QQ的友盟反馈界面(MVP模式)

    什么是MVP呢,简单来说就是将view层和逻辑完全独立出来,让逻辑和显示完全独立。本例中就是采用了这种模式,让activity作为view层,activity中涉及了适配器,所以这里尝试让适配器作为P层来进行逻辑处理。以后可能要考虑用多个p来做逻辑处理。总之,我们先来分析下如何用MVP得思路来分析这个工程吧~

    一、界面

    界面这个环节有很多细节需要扣,之前我写过一篇文章就是讲这个界面实现的,推荐先去看看:http://www.cnblogs.com/tianzhijiexian/p/4295195.html

    二、根据界面来思考逻辑

    一般情况下我们从设计那里得到了一张图后就需要进行分析了,分析这个界面需要什么逻辑。所以我们再来看看界面是什么样的:

     

    我们从上到下进行分析,分析时就需要写出接口了,等于把自然语言程序化。

    1.顶部有退出按钮——finish()

    2.顶部有刷新按钮——refresh();刷新成功后需要有回调——onRefreshSuccess()

    3.界面有信息,需要适配器——setAdapter()

    4.下方有+号,是发送图片的按钮——sendPhoto();因为是实时聊天界面,即使信息没发送成功,也需要添加到适配器中,显示一个没法送成功的标记就好。所以不需要做回调。只需要在适配器的getView()中根据list得item的标志来判断是否发送成功了,根据是否发送成功来显示没法送成功的感叹号。

    5.有发送按钮,发送文字——addNewReply(String str);因为是实时聊天界面,即使信息没发送成功,也需要添加到适配器中,显示一个没法送成功的标记就好。需要在适配器的getView()中进行状态的回调,回调方式同上。

    6.既然需要在getView中进行回调,那么activity中就要能有这个getView()的方法——onGetViewFromAdapter()

    三、Activity需要实现的接口

    这样我们大概的接口就已经写好了,下面来看看最终的接口文档:

    接口 IUMengFeedbackView(实现友盟反馈的activity需要实现这个接口)

    四、调用P层进行逻辑操作

    我们假设我们的activity已经实现了这个接口,也已经做好了界面,那么是不是该调用p层来处理逻辑了呢?现在p层在哪里呢?别着急,p我已经写好了,而且对外提供了很多的方法让view可以随意调用。

    UMengFeedbackPresenter的源码:

    package com.kale.umenglib;
    
    import com.umeng.fb.FeedbackAgent;
    import com.umeng.fb.SyncListener;
    import com.umeng.fb.model.Conversation;
    import com.umeng.fb.model.Reply;
    
    import android.content.Context;
    import android.content.Intent;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    
    import java.util.List;
    import java.util.UUID;
    
    /**
     * @author:Jack Tony
     * 定义回话界面的adapter
     * @date :2015年2月9日
     */
    public class UMengFeedbackPresenter extends BaseAdapter {
    
        private final String TAG = getClass().getSimpleName();
    
        /**
         * 表示是一对一聊天,有两个类型的信息
         */
        private static final int VIEW_TYPE_COUNT = 2;
    
        /**
         * 用户的标识
         */
        private static final int VIEW_TYPE_USER = 0;
    
        /**
         * 开发者的标识
         */
        private static final int VIEW_TYPE_DEV = 1;
    
        /**
         * 一次性加载多少条数据,默认10条
         */
        private int mLoadDataNum = 10; // default
    
        /**
         * 当前显示的数据条数
         */
        private int mCurrentMsgCount = 10;
    
        private Context mContext;
    
        /**
         * 负责显示umeng反馈界面信息的activity
         */
        private IUMengFeedbackView mFeedbackView;
    
        /**
         * 反馈系统的回话对象
         */
        private Conversation mConversation;
    
        private static UMengFeedbackPresenter instance;
    
        private UMengFeedbackPresenter(IUMengFeedbackView view) {
            mContext = (Context) view;
            mFeedbackView = view;
            mConversation = new FeedbackAgent(mContext).getDefaultConversation();
            mConversation.setOnChangeListener(new Conversation.OnChangeListener() {
    
                @Override
                public void onChange() {
                    // 发送消息后会自动调用此方法,在这里更新下发送状态
                    notifyDataSetChanged();
                }
            });
        }
    
        public static UMengFeedbackPresenter getInstance(IUMengFeedbackView view) {
            if (instance == null) {
                instance = new UMengFeedbackPresenter(view);
            }
            return instance;
        }
    
        /**
         * 得到当前adapt中的数据条数
         *
         * @return 当前adapt中的数据条数
         */
        @Override
        public int getCount() {
            // 如果开始时的数目小于一次性显示的数目,就按照当前的数目显示,否则会数组越界
            int totalCount = mConversation.getReplyList().size();
            if (totalCount < mCurrentMsgCount) {
                mCurrentMsgCount = totalCount;
            }
            return mCurrentMsgCount;
        }
    
        /**
         * @return 当前的position
         * 重要方法,计算出当前的position
         */
        private int getCurrentPosition(int position) {
            int totalCount = mConversation.getReplyList().size();
            if (totalCount < mCurrentMsgCount) {
                mCurrentMsgCount = totalCount;
            }
            return totalCount - mCurrentMsgCount + position;
        }
    
        @Override
        public Object getItem(int position) {
            position = getCurrentPosition(position);
            return mConversation.getReplyList().get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return getCurrentPosition(position);
        }
    
    
        @Override
        public int getViewTypeCount() {
            // 这里是一对一聊天,所以是两种类型
            return VIEW_TYPE_COUNT;
        }
    
        @Override
        public int getItemViewType(int position) {
            position = getCurrentPosition(position);
            // 获取单条回复
            Reply reply = mConversation.getReplyList().get(position);
            if (reply.type.equals(Reply.TYPE_DEV_REPLY)) {
                // 开发者回复Item布局
                return VIEW_TYPE_DEV;
            } else if (reply.type.equals(Reply.TYPE_USER_REPLY)) {
                // 用户反馈、回复Item布局
                return VIEW_TYPE_USER;
            } else {
                return 0;
            }
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            position = getCurrentPosition(position);
            // 得到当前位置的reply对象
            Reply reply = mConversation.getReplyList().get(position);
            //Log.d(TAG, "reply type = " + reply.type);
            if (convertView == null) {
                LayoutInflater inflater = LayoutInflater.from(mContext);
                // 根据Type的类型来加载不同的Item布局
                switch (reply.type) {
                    case Reply.TYPE_DEV_REPLY:
                        // 如果是开发者回复的,那么就加载开发者回复的布局
                        convertView = inflater.inflate(mFeedbackView.getDevReplyLayoutId(), null);
                        break;
                    case Reply.TYPE_NEW_FEEDBACK:
                        //break;
                    case Reply.TYPE_USER_REPLY:
                        convertView = inflater.inflate(mFeedbackView.getUserReplyLayoutId(), null);
                        break;
                    default:
                }
            }
    
            if (reply.type.equals(Reply.TYPE_USER_REPLY) || reply.type.equals(Reply.TYPE_NEW_FEEDBACK)) {
                mFeedbackView.setUserReplyView(convertView, reply);
            } else if (reply.type.equals(Reply.TYPE_DEV_REPLY)) {
                mFeedbackView.setDevReplyView(convertView, reply);
            }
    
            Reply nextReply = null;
            if ((position + 1) < mConversation.getReplyList().size()) {
                nextReply = mConversation.getReplyList().get(position + 1);
            }
            mFeedbackView.onGetViewFromAdapter(convertView, reply, nextReply);
            return convertView;
        }
    
        /**
         * 加载之前的聊天信息
         */
        public void loadOldData() {
            int loadDataNum = mLoadDataNum;
            int totalCount = mConversation.getReplyList().size();
            if (loadDataNum + mCurrentMsgCount >= totalCount) {
                // 如果要加载的数据超过了数据的总量,算出实际加载的数据条数
                loadDataNum = totalCount - mCurrentMsgCount;
            }
            mCurrentMsgCount += loadDataNum;
            notifyDataSetChanged();
            mFeedbackView.onLoadOldDataSuccess(loadDataNum);
        }
    
        /**
         * 发送图片给开发者
         */
        public void sendPhotoToDev() {
            Intent intent = new Intent();
            intent.putExtra(UMengFeedbackPhotoActivity.KEY_UMENG_GET_PHOTO, UMengFeedbackPhotoActivity.VALUE_UMENG_GET_PHOTO);
            intent.setClass(mContext, UMengFeedbackPhotoActivity.class);
            mContext.startActivity(intent);
        }
    
        /**
         * 当用户发送图片信息时,在Activity的onActivityResult中调用此方法来处理上传图片等后续操作
         */
        protected void getPhotoFromAlbum(Intent data) {
            //Log.e(TAG, "data.getDataString -- " + data.getDataString());
            if (UMengB.a(mContext, data.getData())) {
                UMengB.a(mContext, data.getData(), "R" + UUID.randomUUID().toString(), new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        sendMsgToDev((String) msg.obj, Reply.CONTENT_TYPE_IMAGE_REPLY);
                    }
                });
            }
        }
    
        /**
         * 用户发送了一条新的信息后调用此方法
         *
         * @param replyMsg 信息的内容
         * @param type     Reply.CONTENT_TYPE_TEXT_REPLY或者Reply.CONTENT_TYPE_IMAGE_REPLY
         */
        public void sendMsgToDev(String replyMsg, String type) {
            if (type.equals(Reply.CONTENT_TYPE_TEXT_REPLY)) {
                mConversation.addUserReply(replyMsg);
            } else if (type.equals(Reply.CONTENT_TYPE_IMAGE_REPLY)) {
                mConversation.addUserReply("", replyMsg, "image_reply", -1.0F);
            } else if (type.equals(Reply.CONTENT_TYPE_AUDIO_REPLY)) {
    
            }
            mCurrentMsgCount++;
            syncToUmeng();
        }
    
        /**
         * 将数据和服务器同步
         * TODO:这里有两种写法,可以考虑换个实现方式。
         */
        public void syncToUmeng() {
            //new FeedbackAgent(mContext).sync();// 第一种写法
            // 第二种写法↓
            mConversation.sync(new SyncListener() {
    
                @Override
                public void onSendUserReply(List<Reply> replyList) {
                    Log.d(TAG, "onSendUserReply");
                    if (replyList == null || replyList.size() < 1) {
                        Log.d(TAG, "user 用户没有发送新的消息");
                    } else {
                        notifyDataSetChanged();
                    }
                }
    
                @Override
                public void onReceiveDevReply(List<Reply> replyList) {
                    Log.d(TAG, "onReceiveDevReply");
                    if (replyList == null || replyList.size() < 1) {
                        // 没有开发者新的回复
                        Log.d(TAG, "dev 开发者没有新的回复");
                    } else {
                        notifyDataSetChanged();
                    }
                }
            });
        }
    
    
        /**
         * 设置界面一开始显示多少条数据,默认显示最近的十条信息
         */
        public void setReplyMsgCount(int count) {
            mCurrentMsgCount = count;
        }
    
        /**
         * 设置调用loadOldData()时,一次性加载多少条数据,默认10条
         */
        public void setLoadDataNum(int number) {
            mLoadDataNum = number;
        }
    
        /**
         * 得到适配器对象
         */
        public BaseAdapter getAdapter() {
            return this;
        }
    
    }
    shark0017

    因为有发送图片的功能所以需要从系统相册中得到图片,这就要建立一个activity去获取图片:

    package com.kale.umenglib;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.provider.MediaStore;
    
    /**
     * @author Jack Tony
     * @date 2015/5/11
     */
    public class UMengFeedbackPhotoActivity extends Activity{
    
        private static final int REQUEST_CODE = 1;
    
        public static final String KEY_UMENG_GET_PHOTO = "KEY_UMENG_GET_PHOTO";
        public static final int VALUE_UMENG_GET_PHOTO = 1;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            if (getIntent() != null && getIntent().getIntExtra(KEY_UMENG_GET_PHOTO, 0) == VALUE_UMENG_GET_PHOTO) {
                Intent intent = new Intent("android.intent.action.PICK", MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                startActivityForResult(intent, REQUEST_CODE);
            }
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (resultCode == -1 && requestCode == REQUEST_CODE && data != null) {
                UMengFeedbackPresenter.getInstance(null).getPhotoFromAlbum(data);
            }
            finish();
        }
    }

    五、Demo

    下面的demo通过activity来调用了presenter的各个方法,完全自定义的友盟的反馈界面,而且只用关系界面的view,不用考虑任何逻辑处理。这就是mvp的特点,分工明确,而且方便扩展~

    UMengFeedbackActivity源码:

    package com.example.jack.umengfeedback;
    
    import com.kale.lib.ViewHolder;
    import com.kale.lib.activity.KaleBaseActivity;
    import com.kale.lib.utils.EasyToast;
    import com.kale.lib.utils.InputUtil;
    import com.kale.umenglib.IUMengFeedbackView;
    import com.kale.umenglib.UMengFeedbackPresenter;
    import com.umeng.fb.model.Reply;
    
    import android.app.Activity;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v4.widget.SwipeRefreshLayout;
    import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
    import android.text.Editable;
    import android.text.TextUtils;
    import android.text.TextWatcher;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.view.KeyEvent;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.View.OnKeyListener;
    import android.view.ViewStub;
    import android.view.WindowManager;
    import android.widget.AbsListView;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.ImageView;
    import android.widget.ListView;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class UMengFeedbackActivity extends KaleBaseActivity implements IUMengFeedbackView {
    
        /**
         * 退出的文字按钮
         */
        private TextView exitTv;
    
        /**
         * 刷新新的信息的图片按钮
         */
        private ImageView refreshIv;
    
        /**
         * 会话界面的下拉刷新控件
         */
        private SwipeRefreshLayout swipeRefreshLayout;
    
        /**
         * 对话的listView
         */
        private ListView conversationLv;
    
        /**
         * 输入信息的文本框
         */
        private EditText inputBoxEt;
    
        /**
         * 发送信息的按钮
         */
        private Button sendMsgBtn;
    
        private ImageView sendPhotoIv;
    
        /**
         * 友盟反馈界面的聊天信息适配器
         */
        private UMengFeedbackPresenter mUMengFeedbackPresenter;
    
    
        @Override
        protected void beforeSetContentView() {
            super.beforeSetContentView();
        }
    
        @Override
        protected int getContentViewId() {
            return R.layout.umeng_feedback_main;
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    
    
        /**
         * 当这个activity在最上方时不重复启动activity, 如果调用了startActivity,那么就更新下视图
         */
        @Override
        protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
            Log.d(TAG, "on new intent");
        }
    
        @Override
        protected void findViews() {
            refreshIv = getView(R.id.refresh_imageView);
            exitTv = getView(R.id.exit_textView);
            swipeRefreshLayout = getView(R.id.swipe_container);
            conversationLv = getView(R.id.fb_conversation_listView);
            inputBoxEt = getView(R.id.inputBox_editText);
            sendMsgBtn = getView(R.id.sendMsg_button);
            sendPhotoIv = getView(R.id.sendPhoto_imageView);
        }
    
        @Override
        protected void beforeSetViews() {
            mUMengFeedbackPresenter = UMengFeedbackPresenter.getInstance(this);
        }
    
        @Override
        protected void setViews() {
            refreshIv.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    // TODO:修复bug:只有少数信息时,刷新开发者的回复后listView会跳到顶端
                    mUMengFeedbackPresenter.syncToUmeng();
                    EasyToast.makeText(mContext, "刷新成功~");
                }
            });
            exitTv.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    finish();
                }
            });
            setSwipeLayout();
            setConversationListView();
            setInputBoxEditText();
            /**
             * 设置发送按钮的事件
             */
            sendMsgBtn.setEnabled(false);
            sendMsgBtn.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    sendMessage();
                }
            });
            /**
             * 设置添加图片按钮的事件
             */
            sendPhotoIv.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mUMengFeedbackPresenter.sendPhotoToDev();
                    conversationLv.setSelection(mUMengFeedbackPresenter.getAdapter().getCount());
                }
            });
        }
    
        /**
         * 设置下拉刷新的控件
         */
        private void setSwipeLayout() {
            swipeRefreshLayout.setSize(SwipeRefreshLayout.DEFAULT);
            // 设置下拉圆圈上的颜色,蓝色、绿色、橙色、红色
            swipeRefreshLayout.setColorSchemeResources(
                    android.R.color.holo_blue_bright,
                    android.R.color.holo_green_light,
                    android.R.color.holo_orange_light,
                    android.R.color.holo_red_light);
            swipeRefreshLayout.setOnRefreshListener(new OnRefreshListener() {
    
                @Override
                public void onRefresh() {
                    mUMengFeedbackPresenter.loadOldData();
                    conversationLv.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_NORMAL);
                }
            });
        }
    
        /**
         * 当加载旧的数据完成后的回调方法
         *
         * @param loadDataNum 加载了多少个旧的数据
         */
        @Override
        public void onLoadOldDataSuccess(int loadDataNum) {
            swipeRefreshLayout.setRefreshing(false);
            // 加载完毕旧的数据,跳到刷新出来数据的位置
            if (loadDataNum - 1 >= 0) {
                conversationLv.setSelection(loadDataNum - 1);
            } else {
                EasyToast.makeText(mContext, "已经到头了");
                conversationLv.setSelection(0);
            }
            conversationLv.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
        }
    
        /**
         * 设置listView,list不显示分割线,设置滚动监听器,设置适配器
         */
        private void setConversationListView() {
            conversationLv.setDivider(null);
            conversationLv.setAdapter(mUMengFeedbackPresenter.getAdapter());
            // 不显示滚动到顶部/底部的阴影(减少绘制)
            conversationLv.setOverScrollMode(View.OVER_SCROLL_NEVER);
            // 监听listView的滑动状态,如果到了顶部就刷新数据,向上滑动就隐藏输入法
            conversationLv.setOnScrollListener(new AbsListView.OnScrollListener() {
                @Override
                public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}
    
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {
                    switch (scrollState) {
                        case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
                            // 滚动停止
                            if (view.getLastVisiblePosition() == (view.getCount() - 1)) {
                                // 如果滚动到底部
                            } else if (view.getFirstVisiblePosition() == 0) {
                                // 滚动到顶部
                            }
                            break;
                        case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
                            // 开始滚动
                            break;
                        case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
                            // 正在滚动
                            InputUtil.getInstance((Activity) mContext).hide();
                            break;
                    }
                }
            });
        }
    
    
        /**
         * 设置发送消息的按钮和输入框 按下回车键,发送消息
         */
        private void setInputBoxEditText() {
    
            inputBoxEt.setOnKeyListener(new OnKeyListener() {
    
                @Override
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    // 按下回车发送消息
                    // 这两个条件必须同时成立,如果仅仅用了enter判断,就会执行两次
                    if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
                        sendMessage();
                        return true;
                    }
                    return false;
                }
            });
            // 给editText添加监听器
            inputBoxEt.addTextChangedListener(new TextWatcher() {
    
                // 输入过程中,还在内存里,没到屏幕上
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                }
    
                // 在输入之前会触发的
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                }
    
                @Override
                public void afterTextChanged(Editable s) {
                    // 输入完将要显示到屏幕上时会触发
                    boolean isEmpty = s.toString().isEmpty();
                    sendMsgBtn.setEnabled(!isEmpty);
                    sendMsgBtn.setTextColor(isEmpty ? 0xffa1a2a5 : 0xffffffff);
                }
            });
        }
    
        /**
         * 发送消息
         */
        private void sendMessage() {
            String replyMsg = inputBoxEt.getText().toString();
            inputBoxEt.getText().clear();
            if (!TextUtils.isEmpty(replyMsg)) {
                mUMengFeedbackPresenter.sendMsgToDev(replyMsg, Reply.CONTENT_TYPE_TEXT_REPLY);
                conversationLv.setSelection(mUMengFeedbackPresenter.getAdapter().getCount());
            }
        }
    
        @Override
        public int getUserReplyLayoutId() {
            return R.layout.umeng_feedback_user_reply;
        }
    
        /**
         * 设置用户的list item view
         * 这里的提示信息有进度条和感叹号两种。如果正在发送就显示进度条,如果发送失败就显示感叹号
         */
        @Override
        public void setUserReplyView(View convertView, Reply reply) {
            // 利用viewHolder进行了优化
            TextView textMsgTv = ViewHolder.get(convertView, R.id.textMsg_textView);
            ImageView photoMsgIv = ViewHolder.get(convertView, R.id.photoMsg_imageView);
            ImageView msgErrorIv = ViewHolder.get(convertView, R.id.msg_error_imageView);
            ProgressBar msgSendingPb = ViewHolder.get(convertView, R.id.msg_progressBar);
            // 放入消息
            switch (reply.content_type) {
                case Reply.CONTENT_TYPE_TEXT_REPLY:
                    photoMsgIv.setVisibility(View.GONE);
                    textMsgTv.setVisibility(View.VISIBLE);
                    textMsgTv.setText(reply.content);
                    break;
                case Reply.CONTENT_TYPE_IMAGE_REPLY:
                    textMsgTv.setVisibility(View.GONE);
                    photoMsgIv.setVisibility(View.VISIBLE);
                    // 显示大图
                    //photoMsgIv.setImageBitmap(BitmapFactory.decodeFile(com.umeng.fb.util.c.b(mContext, reply.reply_id)));
                    // 显示小图
                    com.umeng.fb.image.a.a().a(com.umeng.fb.util.c.b(mContext, reply.reply_id), photoMsgIv, getPhotoSize(mContext));
                    break;
                case Reply.CONTENT_TYPE_AUDIO_REPLY:
    
                    break;
                default:
            }
    
            // 根据Reply的状态来设置replyStateFailed的状态,如果发送失败就显示提示图标
            switch (reply.status) {
                case Reply.STATUS_NOT_SENT:
                    msgSendingPb.setVisibility(View.GONE);
                    msgErrorIv.setVisibility(View.VISIBLE);
                    break;
                case Reply.STATUS_SENDING:
                    //break;
                case Reply.STATUS_WILL_SENT:
                    msgSendingPb.setVisibility(View.VISIBLE);
                    msgErrorIv.setVisibility(View.GONE);
                    break;
                case Reply.STATUS_SENT:
                    msgSendingPb.setVisibility(View.GONE);
                    msgErrorIv.setVisibility(View.GONE);
                    break;
                default:
            }
        }
    
        @Override
        public int getDevReplyLayoutId() {
            return R.layout.umeng_feedback_dev_reply;
        }
        
        /**
         * 设置开发者的list item view
         */
        @Override
        public void setDevReplyView(View convertView, Reply reply) {
            // 利用viewHolder进行了优化
            TextView textMsgTv = ViewHolder.get(convertView, R.id.dev_textMsg_textView);
            textMsgTv.setText(reply.content);
        }
    
    
        /**
         * 如果两条信息间隔了TIME_RANGE秒,那么就显示上一条信息的发送时间
         */
        public static final int TIME_RANGE = 2 * 60;
    
        @Override
        public void onGetViewFromAdapter(View convertView, Reply reply, Reply nextReply) {
           /* Log.d(TAG, "context_type = " + reply.content_type);
            Log.d(TAG, "context = " + reply.content);
            Log.d(TAG, "reply_id = " + reply.reply_id);*/
    
            // 显示消息的时间
            if (nextReply != null) {
                ViewStub timeView = ViewHolder.get(convertView, R.id.msg_time_viewStub);
                // 当两条回复相差TIME_RANGE秒时显示时间
                if (nextReply.created_at - reply.created_at > TIME_RANGE * 1000) {
                    timeView.setVisibility(View.VISIBLE);
                    TextView timeTv = ViewHolder.get(convertView, R.id.msg_Time_TextView);
                    Date replyTime = new Date(reply.created_at);
                    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
                    timeTv.setText(sdf.format(replyTime));
                } else {
                    timeView.setVisibility(View.GONE);
                }
            }
        }
    
        private int getPhotoSize(Context context) {
            DisplayMetrics metrics = new DisplayMetrics();
            WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            windowManager.getDefaultDisplay().getMetrics(metrics);
            return metrics.widthPixels > metrics.heightPixels ? metrics.heightPixels : metrics.widthPixels;
        }
    
        @Override
        public void finish() {
            super.finish();
            InputUtil.getInstance((Activity) mContext).hide();
        }
    }

    源码下载:

    http://download.csdn.net/detail/shark0017/8683945

    最新源码(推荐):http://download.csdn.net/detail/shark0017/8686989

    BTW:源码引用了kaleLibrary这个库:https://github.com/tianzhijiexian/KaleLibrary/

  • 相关阅读:
    Windows Server 2019 Core 或 hyper-v server 2019图形工具安装
    添加进站允许规则和出站规则命令 netsh advfirewall firewall
    ID 1196 请确保与从属 IP 地址相关联的网络适配器已配置为至少可以访问一个 DNS 服务器
    通过注册表的方式禁用IPV6
    MS-SQL SERVER服务器占用CPU高,用语句先排查。
    hyper-v server 卸载软件(MD-DOS卸载软件)
    除getopenFilename以外。打开文件的另一种方式FileDialog
    返回当前工作簿的目录ThisWorkbook.path & ""
    筛选
    自动填充
  • 原文地址:https://www.cnblogs.com/tianzhijiexian/p/4492773.html
Copyright © 2011-2022 走看看