打造高仿QQ的友盟反馈界面(MVP模式)

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

一、界面

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

 

二、根据界面来思考逻辑

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

打造高仿QQ的友盟反馈界面(MVP模式) 打造高仿QQ的友盟反馈界面(MVP模式)

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

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需要实现这个接口)

打造高仿QQ的友盟反馈界面(MVP模式)

 

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

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

打造高仿QQ的友盟反馈界面(MVP模式)

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

相关文章:

  • 2022-12-23
  • 2021-06-19
  • 2022-12-23
  • 2021-09-02
  • 2022-12-23
  • 2021-12-05
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2021-04-19
  • 2022-12-23
  • 2022-12-23
  • 2021-10-19
  • 2022-12-23
  • 2022-02-07
相关资源
相似解决方案