/* * The MIT License (MIT) * * Copyright (c) 2014-2015 Umeng, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.umeng.comm.ui.fragments; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import android.content.Intent; import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener; import android.text.TextUtils; import android.util.TypedValue; import android.view.View; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.umeng.comm.core.beans.CommConfig; import com.umeng.comm.core.beans.CommUser; import com.umeng.comm.core.beans.Comment; import com.umeng.comm.core.beans.FeedItem; import com.umeng.comm.core.beans.FeedItem.CATEGORY; import com.umeng.comm.core.beans.Like; import com.umeng.comm.core.constants.Constants; import com.umeng.comm.core.imageloader.UMImageLoader; import com.umeng.comm.core.listeners.Listeners.OnItemViewClickListener; import com.umeng.comm.core.sdkmanager.ImageLoaderManager; import com.umeng.comm.core.utils.CommonUtils; import com.umeng.comm.core.utils.DeviceUtils; import com.umeng.comm.core.utils.Log; import com.umeng.comm.core.utils.ResFinder; import com.umeng.comm.core.utils.ToastMsg; import com.umeng.comm.ui.activities.FeedDetailActivity; import com.umeng.comm.ui.adapters.FeedAdapter; import com.umeng.comm.ui.mvpview.MvpFeedView; import com.umeng.comm.ui.presenter.impl.FeedListPresenter; import com.umeng.comm.ui.utils.BroadcastUtils; import com.umeng.comm.ui.utils.BroadcastUtils.BROADCAST_TYPE; import com.umeng.comm.ui.utils.BroadcastUtils.DefalutReceiver; import com.umeng.comm.ui.utils.Filter; import com.umeng.comm.ui.widgets.RefreshLayout.OnLoadListener; import com.umeng.comm.ui.widgets.RefreshLvLayout; /** * 这是Feed流列表页面,包含当前最新的消息列表.从该页面可以跳转到话题搜索页面、消息发布页面,可以浏览消息流中的图片、评论某项消息、进入某个好友的主页等. */ public abstract class FeedListFragment<P extends FeedListPresenter> extends CommentEditFragment<List<FeedItem>, P> implements MvpFeedView { /** * ImageLoader */ protected UMImageLoader mImageLoader = ImageLoaderManager.getInstance().getCurrentSDK(); /** * 下拉刷新, 上拉加载的布局, 包裹了Feeds ListView */ protected RefreshLvLayout mRefreshLayout; /** * feeds ListView */ protected ListView mFeedsListView; /** * 消息流适配器 */ protected FeedAdapter mFeedLvAdapter; /** * title的文本TextView */ protected TextView mTitleTextView; /** * ListView的footers */ protected List<View> mFooterViews = new ArrayList<View>(); /** * 过滤掉某些关键字的filter */ protected Filter<FeedItem> mFeedFilter; /** * 布局改变时的回调。主要用于监测输入法是否已经打开,并做相关的逻辑处理(评论中某项的具体滚动距离) */ private OnGlobalLayoutListener mOnGlobalLayoutListener; /** * 当前登录的用户 */ protected CommUser mUser = CommConfig.getConfig().loginedUser; /** * 发表feed的button */ protected ImageView mPostBtn; List<String> mTabTitls = new ArrayList<String>(); @Override protected int getFragmentLayout() { return ResFinder.getLayout("umeng_comm_feeds_frgm_layout"); } @Override protected void initWidgets() { super.initWidgets(); // 初始化视图 initViews(); // 初始化Feed Adapter initAdapter(); // 请求中的状态 mRefreshLayout.setRefreshing(true); registerBroadcast(); } /** * 初始化feed流 页面显示相关View */ protected void initViews() { // 初始化刷新相关View跟事件 initRefreshView(); mPostBtn = mViewFinder.findViewById(ResFinder.getId("umeng_comm_new_post_btn")); } /** * 初始化下拉刷新试图, listview */ protected void initRefreshView() { // 下拉刷新, 上拉加载的布局 mRefreshLayout = mViewFinder.findViewById(ResFinder.getId("umeng_comm_swipe_layout")); // 下拉刷新时执行的回调 mRefreshLayout.setOnRefreshListener(new OnRefreshListener() { @Override public void onRefresh() { // 加载最新的feed mPresenter.loadDataFromServer(); } }); // 上拉加载更多 mRefreshLayout.setOnLoadListener(new OnLoadListener() { @Override public void onLoad() { loadMoreFeed(); } }); // 滚动监听器, 滚动停止时才加载图片 mRefreshLayout.addOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) { mImageLoader.resume(); } else { mImageLoader.pause(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }); int feedListViewResId = ResFinder.getId("umeng_comm_feed_listview"); // feed列表 listview mFeedsListView = mRefreshLayout.findRefreshViewById(feedListViewResId); // 添加footer mRefreshLayout.setDefaultFooterView(); // 关闭动画缓存 mFeedsListView.setAnimationCacheEnabled(false); // 开启smooth scrool bar mFeedsListView.setSmoothScrollbarEnabled(true); mFeedsListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (mCommentLayout.getVisibility() == View.VISIBLE) { hideCommentLayout(); return; } final int realPosition = position - mFeedsListView.getHeaderViewsCount(); final FeedItem feedItem = mFeedLvAdapter.getItem(realPosition < 0 ? 0 : realPosition); if (feedItem != null && feedItem.status >= FeedItem.STATUS_SPAM && feedItem.category == CATEGORY.FAVORITES) { ToastMsg.showShortMsgByResName("umeng_comm_feed_spam_deleted"); return; } Intent intent = new Intent(getActivity(), FeedDetailActivity.class); intent.putExtra(Constants.TAG_FEED, feedItem); startActivity(intent); } }); } /** * 隐藏评论的布局跟软键盘</br> */ public void hideCommentLayoutAndInputMethod() { resetCommentLayout(); hideInputMethod(); showPostButtonWithAnim(); } /** * 关闭输入法</br> */ @SuppressWarnings("deprecation") private void hideInputMethod() { if (CommonUtils.isActivityAlive(getActivity())) { sendInputMethodMessage(Constants.INPUT_METHOD_DISAPPEAR, mCommentEditText); mRootView.getRootView().getViewTreeObserver().removeGlobalOnLayoutListener( mOnGlobalLayoutListener); } } /** * */ protected void showPostButtonWithAnim() { } protected void deleteInvalidateFeed(FeedItem feedItem) { // 将无效的feed从listview中删除 mFeedLvAdapter.getDataSource().remove(feedItem); mFeedLvAdapter.notifyDataSetChanged(); } protected void updateAfterDelete(FeedItem feedItem) { mFeedLvAdapter.getDataSource().remove(feedItem); mFeedLvAdapter.notifyDataSetChanged(); // 发送删除广播 BroadcastUtils.sendFeedDeleteBroadcast(getActivity(), feedItem); } /** * 加载更多数据</br> */ protected void loadMoreFeed() { // 没有网络的情况下从数据库加载 if (!DeviceUtils.isNetworkAvailable(getActivity())) { mPresenter.loadDataFromDB(); mRefreshLayout.setLoading(false); return; } mPresenter.fetchNextPageData(); } protected FeedAdapter createListViewAdapter() { return new FeedAdapter(getActivity()); } /** * 初始化适配器 */ protected void initAdapter() { if (mFeedLvAdapter == null) { mFeedLvAdapter = createListViewAdapter(); mFeedLvAdapter.setCommentClickListener(new OnItemViewClickListener<FeedItem>() { @Override public void onItemClick(int position, FeedItem item) { // 如果评论数>0 ,则跳转到详情页面评论;否则直接显示输入法评论 if (item.commentCount > 0) { Intent intent = new Intent(getActivity(), FeedDetailActivity.class); intent.putExtra(Constants.TAG_FEED, item); intent.putExtra(Constants.TAG_IS_SCROLL, true); getActivity().startActivity(intent); return; } mFeedItem = item; if (mCommentPresenter != null) { mCommentPresenter.setFeedItem(item); } showCommentLayout(); addOnGlobalLayoutListener(position); } }); } mRefreshLayout.setAdapter(mFeedLvAdapter); } private void addOnGlobalLayoutListener(final int position) { mOnGlobalLayoutListener = new OnGlobalLayoutListener() { @SuppressWarnings("deprecation") @Override public void onGlobalLayout() { // int count = mFeedsListView.getHeaderViewsCount(); // int headerheight = 0; // if (count > 0) { // for (int i = 0; i < count; i++) { // View view = mFeedsListView.getChildAt(i); // headerheight += view.getHeight(); // } // } // mFeedsListView.setSelectionFromTop(position - 1, headerheight); mRootView.getRootView().getViewTreeObserver() .removeGlobalOnLayoutListener(mOnGlobalLayoutListener); } }; mRootView.getRootView().getViewTreeObserver() .addOnGlobalLayoutListener(mOnGlobalLayoutListener); } @Override public void onResume() { super.onResume(); onBaseResumeDeal(); } /** * 基本的 OnResume处理逻辑</br> */ protected void onBaseResumeDeal() { mFeedsListView.postDelayed(new Runnable() { @Override public void run() { hideInputMethod(); if (mImageLoader != null) { // 启动加载数据 mImageLoader.resume(); } } }, 300); } public void onStop() { resetCommentLayout(); super.onStop(); } protected boolean isMyPage(FeedItem feedItem) { return feedItem != null; } protected void onCancelFollowUser(CommUser user) { Log.d(getTag(), "### cancel follow user"); } /** * 数据同步处理 */ protected DefalutReceiver mReceiver = new DefalutReceiver() { public void onReceiveUser(Intent intent) { BROADCAST_TYPE type = getType(intent); CommUser user = getUser(intent); if (type == BROADCAST_TYPE.TYPE_USER_UPDATE) {// 更新用户信息 updatedUserInfo(user); return; } if (!CommonUtils.isMyself(mUser)) {// 如果不是登录用户,则不remove feed return; } if (type == BROADCAST_TYPE.TYPE_USER_CANCEL_FOLLOW) { // 预留一个hook函数,当取消对某个用户的关注,移除主页上该用户的feed。其他页面不进行操作 onCancelFollowUser(user); } } public void onReceiveFeed(Intent intent) {// 发送or删除时 FeedItem feedItem = getFeed(intent); if (feedItem == null) { return; } BROADCAST_TYPE type = getType(intent); if (BROADCAST_TYPE.TYPE_FEED_POST == type && isMyFeedList()) { postFeedComplete(feedItem); } else if (BROADCAST_TYPE.TYPE_FEED_DELETE == type) { deleteFeedComplete(feedItem); } else if (BROADCAST_TYPE.TYPE_FEED_FAVOURITE == type) { dealFavourite(feedItem); } mFeedsListView.invalidate(); } // 更新Feed的相关数据。包括like、comment、forward数量修改 public void onReceiveUpdateFeed(Intent intent) { FeedItem item = getFeed(intent); List<FeedItem> items = mFeedLvAdapter.getDataSource(); for (FeedItem feed : items) { if (feed.id.equals(item.id)) { // feed = item; feed.isLiked = item.isLiked; feed.likeCount = item.likeCount; feed.likes = item.likes; feed.commentCount = item.commentCount; feed.comments = item.comments; feed.forwardCount = item.forwardCount; feed.category = item.category; break; } } // 此处不可直接调用adapter.notifyDataSetChanged,其他地方在notifyDataSetChanged()方法中又逻辑处理 notifyDataSetChanged(); } }; /** * 在触发收藏操作时,需要收藏feed同步</br> */ protected void dealFavourite(FeedItem feedItem) { } // TODO 此处对于invalidate需要重构 protected void postFeedComplete(FeedItem feedItem) { // mFeedLvAdapter.addToFirst(feedItem); // 此时需要排序,确保置顶的feed放在最前面 mFeedLvAdapter.getDataSource().add(feedItem); mPresenter.sortFeedItems(mFeedLvAdapter.getDataSource()); mFeedLvAdapter.notifyDataSetChanged(); mFeedsListView.setSelection(0); updateForwardCount(feedItem, 1); } protected void deleteFeedComplete(FeedItem feedItem) { mFeedLvAdapter.getDataSource().remove(feedItem); mFeedLvAdapter.notifyDataSetChanged(); updateForwardCount(feedItem, -1); Log.d(getTag(), "### 删除feed"); } protected boolean isMyFeedList() { return true; } /** * 更新转发数</br> * * @param item */ protected void updateForwardCount(FeedItem item, int count) { if (TextUtils.isEmpty(item.sourceFeedId)) { return; } List<FeedItem> items = mFeedLvAdapter.getDataSource(); for (FeedItem feedItem : items) { if (feedItem.id.equals(item.sourceFeedId)) { feedItem.forwardCount = feedItem.forwardCount + count; mFeedLvAdapter.notifyDataSetChanged(); break; } } } /** * 判断该Feed是否来源于特定用户</br> * * @param feedItem * @return */ protected boolean isMyFeed(FeedItem feedItem) { CommUser user = CommConfig.getConfig().loginedUser; if (user == null || TextUtils.isEmpty(user.id)) { return false; } return feedItem.creator.id.equals(user.id); } /** * 用户信息修改以后更新feed的用户信息 * * @param user */ public void updatedUserInfo(CommUser user) { mUser = user; List<FeedItem> feedItems = mFeedLvAdapter.getDataSource(); for (FeedItem feed : feedItems) { updateFeedContent(feed, user); } mFeedLvAdapter.notifyDataSetChanged(); } private void updateFeedContent(FeedItem feed, CommUser user) { if (isMyFeed(feed)) { feed.creator = user; } // 更新like的创建者信息 updateLikeCreator(feed.likes, user); // 更新评论信息 updateCommentCreator(feed.comments, user); // 更新at好友的creator updateAtFriendCreator(feed.atFriends, user); // 转发类型的feed if (feed.sourceFeed != null) { updateFeedContent(feed.sourceFeed, user); } } private void updateLikeCreator(List<Like> likes, CommUser user) { for (Like likeItem : likes) { if (likeItem.creator.id.equals(user.id)) { likeItem.creator = user; } } } private void updateCommentCreator(List<Comment> comments, CommUser user) { for (Comment commentItem : comments) { if (commentItem.creator.id.equals(user.id)) { commentItem.creator = user; } } } private void updateAtFriendCreator(List<CommUser> friends, CommUser user) { for (CommUser item : friends) { if (item.id.equals(user.id)) { item = user; } } } @Override public void postCommentSuccess(Comment comment, CommUser replyUser) { super.postCommentSuccess(comment, replyUser); mFeedLvAdapter.notifyDataSetChanged();// 刷新ListVIew,更新评论数字 mCommentEditText.setText(""); // 清除评论内容 } /** * 设置feed的过滤器</br> * * @param filter */ public void setFeedFilter(Filter<FeedItem> filter) { mFeedFilter = filter; } /** * 过滤数据</br> * * @return */ protected List<FeedItem> filteFeeds(List<FeedItem> list) { List<FeedItem> destList = mFeedFilter != null ? mFeedFilter.doFilte(list) : list; // 移除status>=2的feed,具体值得的含义参考文档说明 Iterator<FeedItem> iterator = destList.iterator(); while (iterator.hasNext()) { FeedItem item = iterator.next(); if (item.status > 1) { iterator.remove(); } } return destList; } private void resetCommentLayout() { if (mCommentLayout != null) { mCommentLayout.setVisibility(View.INVISIBLE); } if (mCommentEditText != null) { mCommentEditText.setText(""); } } protected void registerBroadcast() { // 注册广播接收器 BroadcastUtils.registerUserBroadcast(getActivity(), mReceiver); BroadcastUtils.registerFeedBroadcast(getActivity(), mReceiver); BroadcastUtils.registerFeedUpdateBroadcast(getActivity(), mReceiver); } @Override public void onDestroy() { BroadcastUtils.unRegisterBroadcast(getActivity(), mReceiver); super.onDestroy(); } @Override public void clearListView() { if (mFeedLvAdapter != null) { mFeedLvAdapter.getDataSource().clear(); mFeedLvAdapter.notifyDataSetChanged(); } } @Override public void updateCommentView() { } @Override public void onRefreshStart() { mRefreshLayout.setProgressViewOffset(false, 0, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources() .getDisplayMetrics())); mRefreshLayout.setRefreshing(true); } @Override public void onRefreshEnd() { mRefreshLayout.setRefreshing(false); mRefreshLayout.setLoading(false); } @Override public List<FeedItem> getBindDataSource() { return mFeedLvAdapter.getDataSource(); } @Override public void notifyDataSetChanged() { mFeedLvAdapter.notifyDataSetChanged(); } }