Android仿QQ空間動態(tài)界面分享功能
先看看效果:
用極少的代碼實(shí)現(xiàn)了 動態(tài)詳情 及 二級評論 的 數(shù)據(jù)獲取與處理 和 UI顯示與交互,并且高解耦、高復(fù)用、高靈活。
動態(tài)列表界面MomentListFragment支持 下拉刷新與上拉加載 和 模糊搜索,反復(fù)快速滑動仍然非常流暢。
緩存機(jī)制使得數(shù)據(jù)可在啟動界面后瞬間加載完成。
動態(tài)詳情界面MomentActivity支持 (取消)點(diǎn)贊、(刪除)評論、點(diǎn)擊姓名跳到個人詳情 等。
只有1張圖片時圖片放大顯示,超過1張則按九宮格顯示。
用到的CommentContainerView和MomentView都是獨(dú)立的組件,既可單獨(dú)使用,也可用于ListView或添加至其它ViewGroup等。
CommentContainerView復(fù)用
CommentContainerView.java
setOnCommentClickListener : 設(shè)置點(diǎn)擊評論監(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è)置點(diǎn)擊評論監(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è)置點(diǎn)擊圖片監(jiān)聽 createView : 創(chuàng)建View bindView : 綁定數(shù)據(jù)并顯示View setPraise : 設(shè)置點(diǎn)贊 setShowComment : 設(shè)置是否顯示評論 getShowComment : 獲取是否顯示評論的設(shè)置 setComment : 設(shè)置評論 setPicture : 設(shè)置九宮格圖片 toComment : 跳轉(zhuǎn)到所有評論界面 getData : 獲取動態(tài)綁定的數(shù)據(jù) isLoggedIn : 判斷是否已登錄,未登錄則跳到登錄界面 praise : (取消)點(diǎn)贊 onDialogButtonClick : 處理對話框返回結(jié)果,比如刪除動態(tài) onHttpResponse : 處理Http請求的返回結(jié)果,比如點(diǎn)贊 onClick : 處理點(diǎn)擊事件,比如點(diǎn)擊內(nèi)容跳到動態(tài)詳情界面 onItemClick : 處理點(diǎn)擊圖片的事件,默認(rèn)是查看大圖,可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è)置點(diǎn)擊圖片監(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()); // 點(diǎn)贊 setPraise(data.getIsPraised(), data.getUserList()); // 評論 setComment(data.getCommentItemList()); vMomentViewDivider.setVisibility(llMomentViewPraise.getVisibility() == View.VISIBLE && llMomentViewCommentContainer.getVisibility() == View.VISIBLE ? View.VISIBLE : View.GONE); } /**設(shè)置點(diǎn)贊 * @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; } /**點(diǎn)贊 * @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 ? "點(diǎn)贊" : "取消點(diǎn)贊") + "失敗,請檢查網(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, "點(diǎn)贊的人")); 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>
由于這個項(xiàng)目使用了ZBLibrary快速開發(fā)框架,所以實(shí)現(xiàn)仿QQ空間和微信朋友圈的這種復(fù)雜界面只用了極少的代碼,并且高解耦、高復(fù)用、高靈活。
服務(wù)端是用APIJSON(Server)工程快速搭建的,客戶端App和服務(wù)端通過APIJSON-JSON傳輸結(jié)構(gòu)協(xié)議通信,非常方便靈活,省去了大量的接口和文檔!
今年RxJava特別火,在北京市場幾乎是必備技能,所以我還把這個項(xiàng)目做了個RxJava版本,歡迎交流和指教。
實(shí)現(xiàn)UI的Java類:
MomentListFragment 395行 動態(tài)列表的獲取和顯示 MomentActivity 616行 動態(tài)和評論列表的獲取、顯示和交互(評論和刪除評論等) MomentAdapter 67行 動態(tài)列表的顯示 CommentAdapter 82行 評論列表的顯示 MomentView 495行 動態(tài)的顯示和交互(各種跳轉(zhuǎn)、點(diǎn)贊、刪除等) EmptyEventGridView 56行 動態(tài)里圖片的顯示和交互(觸摸空白處傳遞觸摸事件到內(nèi)層View) PraiseTextView 129行 動態(tài)里點(diǎn)贊用戶的顯示和交互(點(diǎn)擊姓名跳到個人詳情,點(diǎn)擊整體跳到點(diǎn)贊的用戶列表界面) CommentView 153行 一級評論(頭像、姓名、內(nèi)容)的顯示和交互(回復(fù)、刪除等),添加二級評論列表 CommentContainerView 172行 二級評論列表的顯示和交互(查看全部等) CommentTextView 122行 二級評論(姓名、內(nèi)容)的顯示和交互(回復(fù)、刪除等)
實(shí)現(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)容)的顯示
為什么沒有實(shí)現(xiàn)MomentListFragment對應(yīng)的XML布局?
因?yàn)镸omentListFragment繼承BaseHttpListFragment,內(nèi)部用XListView作為缺省列表View,所以可以不用自己實(shí)現(xiàn)了。
實(shí)現(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實(shí)現(xiàn)仿QQ6.0側(cè)滑界面(一)
- Android仿QQ、微信聊天界面長按提示框效果
- Android使用ViewDragHelper實(shí)現(xiàn)QQ6.X最新版本側(cè)滑界面效果實(shí)例代碼
- Android應(yīng)用中使用ViewPager實(shí)現(xiàn)類似QQ的界面切換效果
- Android實(shí)現(xiàn)QQ登錄界面遇到問題及解決方法
- Android QQ新用戶注冊界面繪制
- Android ListView自定義Adapter實(shí)現(xiàn)仿QQ界面
- Android小程序?qū)崿F(xiàn)簡易QQ界面
相關(guān)文章
Android中手機(jī)錄屏并轉(zhuǎn)換GIF的兩種方式
本文主要介紹了android中手機(jī)錄屏并轉(zhuǎn)換GIF的兩種方式,具有一定的參考價值,下面跟著小編一起來看下吧2017-01-01詳解Android通過修改配置文件設(shè)置wifi密碼
這篇文章主要介紹了詳解Android通過修改配置文件設(shè)置wifi密碼的相關(guān)資料,需要的朋友可以參考下2017-07-07淺談AnDroidDraw+DroidDraw實(shí)現(xiàn)Android程序UI設(shè)計(jì)的分析說明
本篇文章是對AnDroidDraw+DroidDraw實(shí)現(xiàn)Android程序UI設(shè)計(jì)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05Android毛玻璃背景效果簡單實(shí)現(xiàn)代碼
這篇文章主要介紹了Android毛玻璃背景效果簡單實(shí)現(xiàn)代碼,需要的朋友可以參考下2017-08-08Android轉(zhuǎn)場效果實(shí)現(xiàn)示例淺析
這篇文章主要為大家介紹了Android轉(zhuǎn)場效果實(shí)現(xiàn)示例淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Android實(shí)現(xiàn)隱藏手機(jī)底部虛擬按鍵
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)隱藏手機(jī)底部虛擬按鍵,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-08-08