先看看效果:
用极少的代码实现了 动态详情 及 二级评论 的 数据获取与处理 和 UI显示与交互,并且高解耦、高复用、高灵活。
动态列表界面MomentListFragment支持 下拉刷新与上拉加载 和 模糊搜索,反复快速滑动仍然非常流畅。
缓存机制使得数据可在启动界面后瞬间加载完成。
动态详情界面MomentActivity支持 (取消)点赞、(删除)评论、点击姓名跳到个人详情 等。
只有1张图片时图片放大显示,超过1张则按九宫格显示。
用到的CommentContainerView和MomentView都是独立的组件,既可单独使用,也可用于ListView或添加至其它ViewGroup等。
CommentContainerView复用
CommentContainerView.java
setOnCommentClickListener : 设置点击评论监听createView : 创建ViewbindView : 绑定数据并显示ViewsetMaxShowCount : 设置最多显示数量,超过则折叠setComment : 设置评论addCommentView : 添加评论View
package apijson.demo.client.view;
import android.annotation.SuppressLint;import android.app.Activity;import android.content.res.Resources;import android.view.LayoutInflater;import android.view.View;import android.view.View.OnClickListener;import android.view.View.OnLongClickListener;import android.view.ViewGroup;import java.util.ArrayList;import java.util.List;import apijson.demo.client.R;import apijson.demo.client.model.CommentItem;import apijson.demo.client.view.CommentView.OnCommentClickListener;import zuo.biao.library.base.BaseView;import zuo.biao.library.util.Log;import zuo.biao.library.util.StringUtil;/**评论容器 * @author Lemon * @useCommentContainerView commentContainerView = new CommentContainerView(context, inflater);adapter中使用convertView = commentContainerView.getView();//[具体见.DemoAdapter] 或 其它类中使用containerView.addView(commentContainerView.getConvertView());commentContainerView.bindView(data);commentContainerView.setOnClickPictureListener(onClickPictureListener);//非必需commentContainerView.setOnDataChangedListener(onDataChangedListener);data = commentContainerView.getData();//非必需commentContainerView.setOnClickListener(onClickListener);//非必需... */public class CommentContainerView extends BaseView<List<CommentItem>> { private static final String TAG = "CommentContainerView"; private OnCommentClickListener onCommentClickListener; /**设置点击评论监听 * @param onCommentClickListener */ public void setOnCommentClickListener(OnCommentClickListener onCommentClickListener) { this.onCommentClickListener = onCommentClickListener; } public CommentContainerView(Activity context, Resources resources) { super(context, resources); } private LayoutInflater inflater; public ViewGroup llCommentContainerViewContainer; public View tvCommentContainerViewMore; @SuppressLint("InflateParams") @Override public View createView(LayoutInflater inflater) { this.inflater = inflater; convertView = inflater.inflate(R.layout.comment_container_view, null); llCommentContainerViewContainer = findViewById(R.id.llCommentContainerViewContainer); tvCommentContainerViewMore = findViewById(R.id.tvCommentContainerViewMore); return convertView; } @Override public void bindView(List<CommentItem> list){ llCommentContainerViewContainer.setVisibility(list == null || list.isEmpty() ? View.GONE : View.VISIBLE); if (list == null) { Log.w(TAG, "bindView data_ == null >> data_ = new List<CommentItem>();"); list = new ArrayList<CommentItem>(); } this.data = list; // 评论 setComment(list); } private int maxShowCount = 3; /**设置最多显示数量,超过则折叠 * @param maxShowCount <= 0 ? 显示全部 : 超过则折叠 */ public void setMaxShowCount(int maxShowCount) { this.maxShowCount = maxShowCount; } /**设置评论 * @param list */ public void setComment(List<CommentItem> list) { int count = list == null ? 0 : list.size(); boolean showMore = maxShowCount > 0 && count > maxShowCount; tvCommentContainerViewMore.setVisibility(showMore ? View.VISIBLE : View.GONE); llCommentContainerViewContainer.removeAllViews(); llCommentContainerViewContainer.setVisibility(count <= 0 ? View.GONE : View.VISIBLE); if (count > 0) { if (showMore) { list = list.subList(0, maxShowCount); } for (int i = 0; i < list.size(); i++) { addCommentView(i, list.get(i)); } } } /**添加评论 * @param index * @param comment */ @SuppressLint("InflateParams") private void addCommentView(final int index, final CommentItem comment) { if (comment == null) { Log.e(TAG, "addCommentView comment == null >> return; "); return; } String content = StringUtil.getTrimedString(comment.getComment().getContent()); if (StringUtil.isNotEmpty(content, true) == false) { Log.e(TAG, "addCommentView StringUtil.isNotEmpty(content, true) == false >> return; "); return; } CommentTextView commentView = (CommentTextView) inflater.inflate(R.layout.comment_item, null); commentView.setView(comment); if (onCommentClickListener != null) { commentView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { onCommentClickListener.onCommentClick(comment, position, index, false); } }); commentView.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { onCommentClickListener.onCommentClick(comment, position, index, true); return true; } }); } llCommentContainerViewContainer.addView(commentView); }}
comment_container_view.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" style="@style/ll_vertical_match_wrap" > <LinearLayout android:id="@+id/llCommentContainerViewContainer" style="@style/ll_vertical_match_wrap" > </LinearLayout> <TextView android:id="@+id/tvCommentContainerViewMore" style="@style/text_small_blue" android:layout_width="match_parent" android:background="@drawable/bg_item_to_alpha" android:gravity="left|center_vertical" android:paddingBottom="4dp" android:paddingTop="4dp" android:text="查看全部" /></LinearLayout>
MomentView复用
MomentView.java
setOnPictureClickListener : 设置点击图片监听createView : 创建ViewbindView : 绑定数据并显示ViewsetPraise : 设置点赞setShowComment : 设置是否显示评论getShowComment : 获取是否显示评论的设置setComment : 设置评论setPicture : 设置九宫格图片toComment : 跳转到所有评论界面getData : 获取动态绑定的数据isLoggedIn : 判断是否已登录,未登录则跳到登录界面praise : (取消)点赞onDialogButtonClick : 处理对话框返回结果,比如删除动态onHttpResponse : 处理Http请求的返回结果,比如点赞onClick : 处理点击事件,比如点击内容跳到动态详情界面onItemClick : 处理点击图片的事件,默认是查看大图,可setOnPictureClickListener接管处理
package apijson.demo.client.view;
import android.annotation.SuppressLint;import android.app.Activity;import android.content.res.Resources;import android.view.LayoutInflater;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.GridView;import android.widget.ImageView;import android.widget.LinearLayout.LayoutParams;import android.widget.TextView;import java.util.ArrayList;import java.util.List;import apijson.demo.client.R;import apijson.demo.client.activity_fragment.LoginActivity;import apijson.demo.client.activity_fragment.MomentActivity;import apijson.demo.client.activity_fragment.UserActivity;import apijson.demo.client.activity_fragment.UserListActivity;import apijson.demo.client.application.APIJSONApplication;import apijson.demo.client.model.CommentItem;import apijson.demo.client.model.Moment;import apijson.demo.client.model.MomentItem;import apijson.demo.client.model.User;import apijson.demo.client.util.HttpRequest;import apijson.demo.client.view.CommentView.OnCommentClickListener;import zuo.biao.apijson.JSONResponse;import zuo.biao.library.base.BaseView;import zuo.biao.library.manager.CacheManager;import zuo.biao.library.manager.HttpManager.OnHttpResponseListener;import zuo.biao.library.model.Entry;import zuo.biao.library.ui.AlertDialog;import zuo.biao.library.ui.AlertDialog.OnDialogButtonClickListener;import zuo.biao.library.ui.GridAdapter;import zuo.biao.library.ui.WebViewActivity;import zuo.biao.library.util.ImageLoaderUtil;import zuo.biao.library.util.Log;import zuo.biao.library.util.ScreenUtil;import zuo.biao.library.util.StringUtil;import zuo.biao.library.util.TimeUtil;/**动态 * @author Lemon * @useMomentView momentView = new MomentView(context, inflater);adapter中使用convertView = momentView.getView();//[具体见.DemoAdapter] 或 其它类中使用containerView.addView(momentView.getConvertView());momentView.bindView(data);momentView.setOnPictureClickListener(onPictureClickListener);//非必需momentView.setOnDataChangedListener(onDataChangedListener);data = momentView.getData();//非必需momentView.setOnClickListener(onClickListener);//非必需... */public class MomentView extends BaseView<MomentItem> implements OnClickListener, OnHttpResponseListener, OnDialogButtonClickListener, OnItemClickListener { private static final String TAG = "MomentView"; public interface OnPictureClickListener { void onClickPicture(int momentPosition, MomentView momentView, int pictureIndex); } private OnPictureClickListener onPictureClickListener; /**设置点击图片监听 * @param onPictureClickListener */ public void setOnPictureClickListener(OnPictureClickListener onPictureClickListener) { this.onPictureClickListener = onPictureClickListener; } public MomentView(Activity context, Resources resources) { super(context, resources); } //UI显示区(操作UI,但不存在数据获取或处理代码,也不存在事件监听代码)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< private LayoutInflater inflater; public View llMomentViewContainer; public ImageView ivMomentViewHead; public TextView tvMomentViewName; public TextView tvMomentViewStatus; public TextView tvMomentViewContent; public GridView gvMomentView; public TextView tvMomentViewDate; public ImageView ivMomentViewPraise; public ImageView ivMomentViewComment; public ViewGroup llMomentViewPraise; public PraiseTextView tvMomentViewPraise; public View vMomentViewDivider; public ViewGroup llMomentViewCommentContainer; @SuppressLint("InflateParams") @Override public View createView(LayoutInflater inflater) { this.inflater = inflater; convertView = inflater.inflate(R.layout.moment_view, null); llMomentViewContainer = findViewById(R.id.llMomentViewContainer); ivMomentViewHead = findViewById(R.id.ivMomentViewHead, this); tvMomentViewName = findViewById(R.id.tvMomentViewName, this); tvMomentViewStatus = findViewById(R.id.tvMomentViewStatus, this); tvMomentViewContent = findViewById(R.id.tvMomentViewContent, this); gvMomentView = findViewById(R.id.gvMomentView); tvMomentViewDate = findViewById(R.id.tvMomentViewDate); ivMomentViewPraise = findViewById(R.id.ivMomentViewPraise, this); ivMomentViewComment = findViewById(R.id.ivMomentViewComment, this); llMomentViewPraise = findViewById(R.id.llMomentViewPraise, this); tvMomentViewPraise = findViewById(R.id.tvMomentViewPraise, this); vMomentViewDivider = findViewById(R.id.vMomentViewDivider); llMomentViewCommentContainer = findViewById(R.id.llMomentViewCommentContainer); return convertView; } private User user; private Moment moment; private long momentId; private long userId; private boolean isCurrentUser; private int status; public int getStatus() { return status; } @Override public void bindView(MomentItem data_){ this.data = data_; llMomentViewContainer.setVisibility(data == null ? View.GONE : View.VISIBLE); if (data == null) { Log.w(TAG, "bindView data == null >> return;"); return; } this.user = data.getUser(); this.moment = data.getMoment(); this.momentId = moment.getId(); this.userId = moment.getUserId(); this.isCurrentUser = APIJSONApplication.getInstance().isCurrentUser(moment.getUserId()); this.status = data.getMyStatus(); ImageLoaderUtil.loadImage(ivMomentViewHead, user.getHead()); tvMomentViewName.setText(StringUtil.getTrimedString(user.getName())); tvMomentViewStatus.setText(StringUtil.getTrimedString(data.getStatusString())); tvMomentViewStatus.setVisibility(isCurrentUser ? View.VISIBLE : View.GONE); tvMomentViewContent.setVisibility(StringUtil.isNotEmpty(moment.getContent(), true) ? View.VISIBLE : View.GONE); tvMomentViewContent.setText(StringUtil.getTrimedString(moment.getContent())); tvMomentViewDate.setText(TimeUtil.getSmartDate(moment.getDate())); // 图片 setPicture(moment.getPictureList()); // 点赞 setPraise(data.getIsPraised(), data.getUserList()); // 评论 setComment(data.getCommentItemList()); vMomentViewDivider.setVisibility(llMomentViewPraise.getVisibility() == View.VISIBLE && llMomentViewCommentContainer.getVisibility() == View.VISIBLE ? View.VISIBLE : View.GONE); } /**设置点赞 * @param joined * @param list */ private void setPraise(boolean joined, List<User> list) { ivMomentViewPraise.setImageResource(joined ? R.drawable.praised : R.drawable.praise); llMomentViewPraise.setVisibility(list == null || list.isEmpty() ? View.GONE : View.VISIBLE); if (llMomentViewPraise.getVisibility() == View.VISIBLE) { tvMomentViewPraise.setView(list); } } private boolean showComment = true; public void setShowComment(boolean showComment) { this.showComment = showComment; } public boolean getShowComment() { return showComment; } public CommentContainerView commentContainerView; /**设置评论 * @param list */ public void setComment(List<CommentItem> list) { llMomentViewCommentContainer.setVisibility(showComment == false || list == null || list.isEmpty() ? View.GONE : View.VISIBLE); if (llMomentViewCommentContainer.getVisibility() != View.VISIBLE) { Log.i(TAG, "setComment llMomentViewCommentContainer.getVisibility() != View.VISIBLE >> return;"); return; } if (commentContainerView == null) { commentContainerView = new CommentContainerView(context, resources); llMomentViewCommentContainer.removeAllViews(); llMomentViewCommentContainer.addView(commentContainerView.createView(inflater)); commentContainerView.setOnCommentClickListener(new OnCommentClickListener() { @Override public void onCommentClick(CommentItem item, int position, int index, boolean isLong) { toComment(item, true); } }); commentContainerView.tvCommentContainerViewMore.setOnClickListener(this); commentContainerView.setMaxShowCount(5); } commentContainerView.bindView(list); } private GridAdapter adapter; /**设置图片 * @param pictureList */ private void setPicture(List<String> pictureList) { List<Entry<String, String>> keyValueList = new ArrayList<Entry<String, String>>(); if (pictureList != null) { for (String picture : pictureList) { keyValueList.add(new Entry<String, String>(picture, null)); } } int pictureNum = keyValueList.size(); gvMomentView.setVisibility(pictureNum <= 0 ? View.GONE : View.VISIBLE); if (pictureNum <= 0) { Log.i(TAG, "setList pictureNum <= 0 >> lvModel.setAdapter(null); return;"); adapter = null; gvMomentView.setAdapter(null); return; } gvMomentView.setNumColumns(pictureNum <= 1 ? 1 : 3); if (adapter == null) { adapter = new GridAdapter(context).setHasName(false); gvMomentView.setAdapter(adapter); } adapter.refresh(keyValueList); gvMomentView.setOnItemClickListener(this); final int gridViewHeight = (int) (ScreenUtil.getScreenSize(context)[0] - convertView.getPaddingLeft() - convertView.getPaddingRight() - getDimension(R.dimen.moment_view_head_width)); try { if (pictureNum >= 7) { gvMomentView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, gridViewHeight)); } else if (pictureNum >= 4) { gvMomentView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, (gridViewHeight*2)/3)); } else if (pictureNum >= 2) { gvMomentView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, gridViewHeight / 3)); } else { gvMomentView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); } } catch (Exception e) { Log.e(TAG, " setPictureGrid try int gridViewHeight;...>> catch" + e.getMessage()); } } /**跳转到所有评论界面 * @param isToComment */ private void toComment(boolean isToComment) { toComment(null, isToComment); } /**跳转到所有评论界面 * @param commentItem * @param isToComment comment有效时为true */ private void toComment(CommentItem commentItem, boolean isToComment) { if (commentItem == null) { commentItem = new CommentItem(); } toActivity(MomentActivity.createIntent(context, momentId, isToComment , commentItem.getId(), commentItem.getUser().getId(), commentItem.getUser().getName())); } //UI显示区(操作UI,但不存在数据获取或处理代码,也不存在事件监听代码)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//Data数据区(存在数据获取或处理代码,但不存在事件监听代码)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @Override public MomentItem getData() {//bindView(null)不会使data == null return llMomentViewContainer.getVisibility() == View.VISIBLE ? data : null; } /**判断是否已登录,如果未登录则弹出登录界面 * @return */ private boolean isLoggedIn() { boolean isLoggedIn = APIJSONApplication.getInstance().isLoggedIn(); if (isLoggedIn == false) { context.startActivity(LoginActivity.createIntent(context)); context.overridePendingTransition(R.anim.bottom_push_in, R.anim.hold); } return isLoggedIn; } /**点赞 * @param toPraise */ public void praise(boolean toPraise) { if (data == null || toPraise == data.getIsPraised()) { Log.e(TAG, "praiseWork toPraise == moment.getIsPraise() >> return;"); return; } // setPraise(toPraise, data.getPraiseCount() + (toPraise ? 1 : -1)); HttpRequest.praiseMoment(momentId, toPraise, toPraise ? HTTP_PRAISE : HTTP_CANCEL_PRAISE, this); } //Data数据区(存在数据获取或处理代码,但不存在事件监听代码)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//Event事件监听区(只要存在事件监听代码就是)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @Override public void onDialogButtonClick(int requestCode, boolean isPositive) { if (isPositive && data != null) { data.setMyStatus(MomentItem.STATUS_DELETING); bindView(data); HttpRequest.deleteMoment(moment.getId(), HTTP_DELETE, this); } } public static final int HTTP_PRAISE = 1; public static final int HTTP_CANCEL_PRAISE = 2; public static final int HTTP_DELETE = 3; @Override public void onHttpResponse(int requestCode, String result, Exception e) { if (data == null) { Log.e(TAG, "onHttpResponse data == null >> return;"); return; } JSONResponse response = new JSONResponse(result); JSONResponse response2 = response.getJSONResponse(Moment.class.getSimpleName()); boolean isSucceed = JSONResponse.isSucceed(response2); switch (requestCode) { case HTTP_PRAISE: case HTTP_CANCEL_PRAISE: if (isSucceed) { data.setIsPraised(requestCode == HTTP_PRAISE); bindView(data); } else { showShortToast((requestCode == HTTP_PRAISE ? "点赞" : "取消点赞") + "失败,请检查网络后重试"); } break; case HTTP_DELETE: showShortToast(isSucceed ? R.string.delete_succeed : R.string.delete_failed); //只对adapter.getCount()有影响。目前是隐藏的,不需要通知,也不需要刷新adapter,用户手动刷新后自然就更新了。 if (isSucceed) { bindView(null); status = MomentItem.STATUS_DELETED; if (onDataChangedListener != null) { onDataChangedListener.onDataChanged(); } CacheManager.getInstance().remove(MomentItem.class, "" + momentId); } else { data.setMyStatus(MomentItem.STATUS_NORMAL); bindView(data); } break; } } @Override public void onClick(View v) { if (data == null) { return; } if (status == MomentItem.STATUS_PUBLISHING) { showShortToast(R.string.publishing); return; } switch (v.getId()) { case R.id.ivMomentViewHead: case R.id.tvMomentViewName: toActivity(UserActivity.createIntent(context, userId)); break; case R.id.tvMomentViewStatus: if (status == MomentItem.STATUS_NORMAL) { new AlertDialog(context, "", "删除动态", true, 0, this).show(); } break; case R.id.tvMomentViewContent: case R.id.tvCommentContainerViewMore: toComment(false); break; case R.id.tvMomentViewPraise: case R.id.llMomentViewPraise: toActivity(UserListActivity.createIntent(context, data.getPraiseUserIdList()) .putExtra(UserListActivity.INTENT_TITLE, "点赞的人")); break; default: if (isLoggedIn() == false) { return; } switch (v.getId()) { case R.id.ivMomentViewPraise: praise(! data.getIsPraised()); break; case R.id.ivMomentViewComment: toComment(true); break; default: break; } break; } } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (status == MomentItem.STATUS_PUBLISHING) { showShortToast(R.string.publishing); return; } if (onPictureClickListener != null) { onPictureClickListener.onClickPicture(this.position, this, position); } else { toActivity(WebViewActivity.createIntent(context, null , adapter == null ? null : adapter.getItem(position).getKey())); } } //Event事件监听区(只要存在事件监听代码就是)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>}
moment_view.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" style="@style/match_wrap" android:descendantFocusability="blocksDescendants" > <LinearLayout android:id="@+id/llMomentViewContainer" style="@style/ll_horizontal_match_wrap" android:background="@color/white" android:gravity="top" android:padding="10dp" > <RelativeLayout android:id="@+id/rlMomentViewItemHead" android:layout_width="@dimen/moment_view_head_width" android:layout_height="@dimen/moment_view_head_height" android:paddingRight="@dimen/moment_view_head_padding_right" > <ImageView android:background="@color/alpha_3" android:id="@+id/ivMomentViewHead" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" /> </RelativeLayout> <LinearLayout style="@style/ll_vertical_match_wrap" android:layout_below="@+id/rlMomentViewItemHead" android:layout_toRightOf="@+id/rlMomentViewItemHead" android:gravity="left" > <LinearLayout style="@style/ll_horizontal_match_wrap" android:layout_height="match_parent" > <TextView android:id="@+id/tvMomentViewName" style="@style/text_small_blue" android:layout_width="match_parent" android:layout_weight="1" android:background="@drawable/bg_item_to_alpha" android:gravity="left" android:text="Name" /> <TextView android:id="@+id/tvMomentViewStatus" style="@style/text_small_blue" android:background="@drawable/bg_item_to_alpha" android:text="发布中" /> </LinearLayout> <TextView android:id="@+id/tvMomentViewContent" style="@style/text_small_black" android:layout_width="match_parent" android:layout_marginTop="5dp" android:background="@drawable/bg_item_to_alpha" android:gravity="left|top" android:maxLines="8" android:paddingBottom="5dp" android:text="This is a content..." /> <apijson.demo.client.view.EmptyEventGridView android:id="@+id/gvMomentView" style="@style/wrap_wrap" android:focusable="false" android:horizontalSpacing="4dp" android:listSelector="@drawable/bg_item_to_alpha" android:numColumns="3" android:paddingTop="4dp" android:scrollbars="none" android:stretchMode="columnWidth" android:verticalSpacing="4dp" /> <LinearLayout style="@style/ll_horizontal_match_wrap" android:layout_height="wrap_content" android:layout_marginTop="5dp" > <TextView android:id="@+id/tvMomentViewDate" style="@style/text_small_black" android:layout_width="match_parent" android:layout_weight="1" android:gravity="left" android:text="2015年12月" /> <ImageView android:id="@+id/ivMomentViewPraise" style="@style/img_btn" android:layout_marginRight="18dp" android:background="@drawable/bg_item_to_alpha" android:src="@drawable/praise" /> <ImageView android:id="@+id/ivMomentViewComment" style="@style/img_btn" android:background="@drawable/bg_item_to_alpha" android:src="@drawable/comment" /> </LinearLayout> <LinearLayout style="@style/ll_vertical_match_wrap" android:layout_marginTop="5dp" android:background="@color/alpha_1" android:paddingLeft="8dp" android:paddingRight="8dp" > <LinearLayout android:id="@+id/llMomentViewPraise" style="@style/ll_horizontal_match_wrap" android:layout_height="wrap_content" android:layout_marginBottom="4dp" android:layout_marginTop="4dp" android:background="@drawable/bg_item_to_alpha" android:gravity="top" > <ImageView android:layout_width="20dp" android:layout_height="20dp" android:scaleType="fitXY" android:src="@drawable/praise" /> <apijson.demo.client.view.PraiseTextView android:id="@+id/tvMomentViewPraise" style="@style/text_small_blue" android:background="@drawable/bg_item_to_alpha" android:gravity="left|top" android:lineSpacingExtra="4dp" android:text="等觉得很赞" /> </LinearLayout> <View android:id="@+id/vMomentViewDivider" style="@style/divider_horizontal_1px" /> <LinearLayout android:id="@+id/llMomentViewCommentContainer" style="@style/ll_vertical_match_wrap" android:paddingBottom="4dp" android:paddingTop="4dp" > </LinearLayout> </LinearLayout> </LinearLayout> </LinearLayout></RelativeLayout>
由于这个项目使用了ZBLibrary快速开发框架,所以实现仿QQ空间和微信朋友圈的这种复杂界面只用了极少的代码,并且高解耦、高复用、高灵活。
服务端是用APIJSON(Server)工程快速搭建的,客户端App和服务端通过APIJSON-JSON传输结构协议通信,非常方便灵活,省去了大量的接口和文档!
今年RxJava特别火,在北京市场几乎是必备技能,所以我还把这个项目做了个RxJava版本,欢迎交流和指教。
实现UI的Java类:
MomentListFragment 395行 动态列表的获取和显示MomentActivity 616行 动态和评论列表的获取、显示和交互(评论和删除评论等)MomentAdapter 67行 动态列表的显示CommentAdapter 82行 评论列表的显示MomentView 495行 动态的显示和交互(各种跳转、点赞、删除等)EmptyEventGridView 56行 动态里图片的显示和交互(触摸空白处传递触摸事件到内层View)PraiseTextView 129行 动态里点赞用户的显示和交互(点击姓名跳到个人详情,点击整体跳到点赞的用户列表界面)CommentView 153行 一级评论(头像、姓名、内容)的显示和交互(回复、删除等),添加二级评论列表CommentContainerView 172行 二级评论列表的显示和交互(查看全部等)CommentTextView 122行 二级评论(姓名、内容)的显示和交互(回复、删除等)
实现UI的XML布局:
moment_activity 47行 动态和评论列表的显示moment_view 148行 动态的显示comment_view 87行 一级评论(头像、姓名、内容)的显示comment_container_view 20行 二级评论列表的显示comment_item 10行 二级评论(姓名、内容)的显示
为什么没有实现MomentListFragment对应的XML布局?
因为MomentListFragment继承BaseHttpListFragment,内部用XListView作为缺省列表View,所以可以不用自己实现了。
实现数据获取、提交和处理的Java类:
HttpRequest +175行 数据的获取和提交(getMoment,...,deleteComment)CommentUtil 140行 单层评论和和二级评论的处理Comment 56行 评论数据CommentItem 99行 评论的显示和交互数据Moment 43行 动态数据MomentItem 272行 动态的显示和交互数据User 103行 用户数据
(注:未列出的代码文件要么和动态无关,要么APIJSON或ZBLibrary已提供。server.model里的类由服务端提供)
仿QQ空间和微信朋友圈,高解耦高复用高灵活
下载试用(测试服务器地址:139.196.140.118:8080)
源码及文档(记得给个Star哦)
https://github.com/TommyLemon/APIJSON
以上所述是小编给大家介绍的Android仿QQ空间动态界面分享功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对武林网网站的支持!
新闻热点
疑难解答