Android仿QQ空間動態(tài)界面分享功能
先看看效果:






用極少的代碼實現(xiàn)了 動態(tài)詳情 及 二級評論 的 數(shù)據(jù)獲取與處理 和 UI顯示與交互,并且高解耦、高復(fù)用、高靈活。
動態(tài)列表界面MomentListFragment支持 下拉刷新與上拉加載 和 模糊搜索,反復(fù)快速滑動仍然非常流暢。
緩存機制使得數(shù)據(jù)可在啟動界面后瞬間加載完成。

動態(tài)詳情界面MomentActivity支持 (取消)點贊、(刪除)評論、點擊姓名跳到個人詳情 等。
只有1張圖片時圖片放大顯示,超過1張則按九宮格顯示。

用到的CommentContainerView和MomentView都是獨立的組件,既可單獨使用,也可用于ListView或添加至其它ViewGroup等。
CommentContainerView復(fù)用


CommentContainerView.java
setOnCommentClickListener : 設(shè)置點擊評論監(jiān)聽 createView : 創(chuàng)建View bindView : 綁定數(shù)據(jù)并顯示View setMaxShowCount : 設(shè)置最多顯示數(shù)量,超過則折疊 setComment : 設(shè)置評論 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
* @use
CommentContainerView 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;
/**設(shè)置點擊評論監(jiān)聽
* @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;
/**設(shè)置最多顯示數(shù)量,超過則折疊
* @param maxShowCount <= 0 ? 顯示全部 : 超過則折疊
*/
public void setMaxShowCount(int maxShowCount) {
this.maxShowCount = maxShowCount;
}
/**設(shè)置評論
* @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復(fù)用


MomentView.java
setOnPictureClickListener : 設(shè)置點擊圖片監(jiān)聽 createView : 創(chuàng)建View bindView : 綁定數(shù)據(jù)并顯示View setPraise : 設(shè)置點贊 setShowComment : 設(shè)置是否顯示評論 getShowComment : 獲取是否顯示評論的設(shè)置 setComment : 設(shè)置評論 setPicture : 設(shè)置九宮格圖片 toComment : 跳轉(zhuǎn)到所有評論界面 getData : 獲取動態(tài)綁定的數(shù)據(jù) isLoggedIn : 判斷是否已登錄,未登錄則跳到登錄界面 praise : (取消)點贊 onDialogButtonClick : 處理對話框返回結(jié)果,比如刪除動態(tài) onHttpResponse : 處理Http請求的返回結(jié)果,比如點贊 onClick : 處理點擊事件,比如點擊內(nèi)容跳到動態(tài)詳情界面 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;
/**動態(tài)
* @author Lemon
* @use
MomentView 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;
/**設(shè)置點擊圖片監(jiān)聽
* @param onPictureClickListener
*/
public void setOnPictureClickListener(OnPictureClickListener onPictureClickListener) {
this.onPictureClickListener = onPictureClickListener;
}
public MomentView(Activity context, Resources resources) {
super(context, resources);
}
//UI顯示區(qū)(操作UI,但不存在數(shù)據(jù)獲取或處理代碼,也不存在事件監(jiān)聽代碼)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
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);
}
/**設(shè)置點贊
* @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;
/**設(shè)置評論
* @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;
/**設(shè)置圖片
* @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());
}
}
/**跳轉(zhuǎn)到所有評論界面
* @param isToComment
*/
private void toComment(boolean isToComment) {
toComment(null, isToComment);
}
/**跳轉(zhuǎn)到所有評論界面
* @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顯示區(qū)(操作UI,但不存在數(shù)據(jù)獲取或處理代碼,也不存在事件監(jiān)聽代碼)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//Data數(shù)據(jù)區(qū)(存在數(shù)據(jù)獲取或處理代碼,但不存在事件監(jiān)聽代碼)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@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數(shù)據(jù)區(qū)(存在數(shù)據(jù)獲取或處理代碼,但不存在事件監(jiān)聽代碼)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//Event事件監(jiān)聽區(qū)(只要存在事件監(jiān)聽代碼就是)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@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 ? "點贊" : "取消點贊") + "失敗,請檢查網(wǎng)絡(luò)后重試");
}
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, "", "刪除動態(tài)", 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事件監(jiān)聽區(qū)(只要存在事件監(jiān)聽代碼就是)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
}
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="發(fā)布中" />
</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快速開發(fā)框架,所以實現(xiàn)仿QQ空間和微信朋友圈的這種復(fù)雜界面只用了極少的代碼,并且高解耦、高復(fù)用、高靈活。
服務(wù)端是用APIJSON(Server)工程快速搭建的,客戶端App和服務(wù)端通過APIJSON-JSON傳輸結(jié)構(gòu)協(xié)議通信,非常方便靈活,省去了大量的接口和文檔!
今年RxJava特別火,在北京市場幾乎是必備技能,所以我還把這個項目做了個RxJava版本,歡迎交流和指教。
實現(xiàn)UI的Java類:
MomentListFragment 395行 動態(tài)列表的獲取和顯示 MomentActivity 616行 動態(tài)和評論列表的獲取、顯示和交互(評論和刪除評論等) MomentAdapter 67行 動態(tài)列表的顯示 CommentAdapter 82行 評論列表的顯示 MomentView 495行 動態(tài)的顯示和交互(各種跳轉(zhuǎn)、點贊、刪除等) EmptyEventGridView 56行 動態(tài)里圖片的顯示和交互(觸摸空白處傳遞觸摸事件到內(nèi)層View) PraiseTextView 129行 動態(tài)里點贊用戶的顯示和交互(點擊姓名跳到個人詳情,點擊整體跳到點贊的用戶列表界面) CommentView 153行 一級評論(頭像、姓名、內(nèi)容)的顯示和交互(回復(fù)、刪除等),添加二級評論列表 CommentContainerView 172行 二級評論列表的顯示和交互(查看全部等) CommentTextView 122行 二級評論(姓名、內(nèi)容)的顯示和交互(回復(fù)、刪除等)
實現(xiàn)UI的XML布局:
moment_activity 47行 動態(tài)和評論列表的顯示 moment_view 148行 動態(tài)的顯示 comment_view 87行 一級評論(頭像、姓名、內(nèi)容)的顯示 comment_container_view 20行 二級評論列表的顯示 comment_item 10行 二級評論(姓名、內(nèi)容)的顯示
為什么沒有實現(xiàn)MomentListFragment對應(yīng)的XML布局?
因為MomentListFragment繼承BaseHttpListFragment,內(nèi)部用XListView作為缺省列表View,所以可以不用自己實現(xiàn)了。
實現(xiàn)數(shù)據(jù)獲取、提交和處理的Java類:
HttpRequest +175行 數(shù)據(jù)的獲取和提交(getMoment,...,deleteComment) CommentUtil 140行 單層評論和和二級評論的處理 Comment 56行 評論數(shù)據(jù) CommentItem 99行 評論的顯示和交互數(shù)據(jù) Moment 43行 動態(tài)數(shù)據(jù) MomentItem 272行 動態(tài)的顯示和交互數(shù)據(jù) User 103行 用戶數(shù)據(jù)
(注:未列出的代碼文件要么和動態(tài)無關(guān),要么APIJSON或ZBLibrary已提供。server.model里的類由服務(wù)端提供)
仿QQ空間和微信朋友圈,高解耦高復(fù)用高靈活

下載試用(測試服務(wù)器地址:139.196.140.118:8080)
源碼及文檔(記得給個Star哦)
https://github.com/TommyLemon/APIJSON
以上所述是小編給大家介紹的Android仿QQ空間動態(tài)界面分享功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
- android 通過向viewpage中添加listview來完成滑動效果(類似于qq滑動界面)
- Android QQ登錄界面繪制代碼
- Android使用ViewDragHelper實現(xiàn)仿QQ6.0側(cè)滑界面(一)
- Android仿QQ、微信聊天界面長按提示框效果
- Android使用ViewDragHelper實現(xiàn)QQ6.X最新版本側(cè)滑界面效果實例代碼
- Android應(yīng)用中使用ViewPager實現(xiàn)類似QQ的界面切換效果
- Android實現(xiàn)QQ登錄界面遇到問題及解決方法
- Android QQ新用戶注冊界面繪制
- Android ListView自定義Adapter實現(xiàn)仿QQ界面
- Android小程序?qū)崿F(xiàn)簡易QQ界面
相關(guān)文章
Android中手機錄屏并轉(zhuǎn)換GIF的兩種方式
本文主要介紹了android中手機錄屏并轉(zhuǎn)換GIF的兩種方式,具有一定的參考價值,下面跟著小編一起來看下吧2017-01-01
詳解Android通過修改配置文件設(shè)置wifi密碼
這篇文章主要介紹了詳解Android通過修改配置文件設(shè)置wifi密碼的相關(guān)資料,需要的朋友可以參考下2017-07-07
淺談AnDroidDraw+DroidDraw實現(xiàn)Android程序UI設(shè)計的分析說明
本篇文章是對AnDroidDraw+DroidDraw實現(xiàn)Android程序UI設(shè)計進行了詳細的分析介紹,需要的朋友參考下2013-05-05
Android轉(zhuǎn)場效果實現(xiàn)示例淺析
這篇文章主要為大家介紹了Android轉(zhuǎn)場效果實現(xiàn)示例淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02

