本文简单模仿微信朋友圈的点赞和评论弹出框,布局等细节请忽略,着重实现弹出框、发评论,及弹出位置的控制。
1. 微信弹出框
微信朋友圈的点赞和评论功能,有2个组成部分:
- 点击左下角的“更多”按钮,弹出对话框;
- 点击评论,弹出输入框,添加评论并在页面中实时显示;
微信朋友圈点赞和评论功能
2. 实际效果
本文将建一个 ListView,在其 Item 中简单模仿微信的布局,然后着重实现弹出窗,并能发评论,忽略具体布局细节。具体效果如下:
丑爆了,我知道了,⊙﹏⊙||
3. 知识点清单
ListView
自定义 Adapter,重写 getView()方法;
PopupWindow
弹出框使用PopupWindow实现,这是点赞和评论的载体,具体要涉及 PopupWindow 点击非窗口位置和再次点击消失以及显示位置的问题(根据相应更多按钮的位置确定 PopupWindow 的显示位置,关于 PopupWindow 的显示位置,可以参考我的另一篇文章 Android PopupWindow 的显示位置);
LayoutInflater
使用LayoutInflater 动态加载PopupWindow 的布局,关于 LayoutInflater 的更多知识,参见我的另一篇博客 Android LayoutInflater ;
Activity 和 Item 的双向通信
通过自定义 OnCommentListener() 来实现 MainActivity(具体来说是屏幕底部评论框中的输入的内容)和 ItemView(动态的获得上述输入的评论内容并展示在该ItemView 中) 的通信,更多知识参见我的另一篇博客《 燕过留声:由 Activity 和 Fragment 的通信方法想到的》;
自定义控件
ListView 中的每个 Item 是一个自定义的 ItemView,记得要重写构造方法,否则会抛出 Android.view.InflateException 异常;
如果想实现微信评论那样用户名和内容回复文字字体颜色不同,而且点击评论用户名触发页面跳转等功能,请参见 《布局优化技巧笔记》 之 ClickableSpan 章节;
4. 美工素材
由于 .apk 本质上是个压缩包,我们可以通过解压得到该 .apk 文件的图片素材和布局文件,更多获得素材的方法参见我的另一篇博文 如何获得Android素材图片。通过这种方式得到颜色、更多按钮的样式等素材,仅供学习之用,请勿做侵犯版权之事。尊重知识版权既是大势所趋,也是终将使每个开发者受益的事。
文件夹r里存放图片
找到更多按钮
5. 关键代码
开发环境:Android Studio 1.4.1 for Mac + ADT 21 + JDK 1.8.0。
MainAcitivity.Java
package main.zhaizu.com.popupwindowdemo;import android.content.Context;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.text.TextUtils;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.EditText;import android.widget.ListView;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import main.zhaizu.com.popupwindowdemo.model.Comment;import main.zhaizu.com.popupwindowdemo.model.Item;import main.zhaizu.com.popupwindowdemo.ui.ItemView;public class MainActivity extends AppCompatActivity { private ListView mListView; private View mCommentView; private MyAdapter myAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mListView = (ListView) findViewById(R.id.listview); myAdapter = new MyAdapter(this, getData()); mListView.setAdapter(myAdapter); mCommentView = findViewById(R.id.comment_view); } private ArrayList<Item> getData() { int ITEM_COUNT = 20; ArrayList<Item> data = new ArrayList<>(); data.add(new Item(R.drawable.xiaona, "薄荷栗", "我学过跆拳道,都给我跪下唱征服", "昨天")); data.add(new Item(R.drawable.xueyan, "欣然", "走遍天涯海角,唯有我家风景最好,啊哈哈", "昨天")); data.add(new Item(R.drawable.leishao, "陈磊_CL", "老子以后要当行长的,都来找我借钱吧,now", "昨天")); data.add(new Item(R.drawable.yuhong, "永恒依然", "房子车子都到碗里来", "昨天")); data.add(new Item(R.drawable.lanshan, "蓝珊", "你们这群傻×,我笑而不语", "昨天")); return data; } private class MyAdapter extends BaseAdapter implements ItemView.OnCommentListener { private Context context; private ArrayList<Item> mData; private Map<Integer, ItemView> mCachedViews = new HashMap<>(); public MyAdapter(Context context, ArrayList<Item> mData) { this.context = context; this.mData = mData; } @Override public int getCount() { return mData.size(); } @Override public Object getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView != null) { view = convertView; } else { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.listview_item, null, false); } if (view instanceof ItemView) { Item data = (Item) getItem(position); ((ItemView) view).setData(data); ((ItemView) view).setPosition(position); ((ItemView) view).setCommentListener(this); cacheView(position, (ItemView) view); } return view; } @Override public void onComment(int position) { showCommentView(position); } private void cacheView(int position, ItemView view) { Iterator<Map.Entry<Integer, ItemView>> entries = mCachedViews.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<Integer, ItemView> entry = entries.next(); if (entry.getValue() == view && entry.getKey() != position) { mCachedViews.remove(entry.getKey()); break; } } mCachedViews.put(position, view); } private void showCommentView(final int position) { mCommentView.setVisibility(View.VISIBLE); mCommentView.findViewById(R.id.submit).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EditText et = (EditText) mCommentView.findViewById(R.id.edit); String s = et.getText().toString(); if (!TextUtils.isEmpty(s)) { Comment comment = new Comment(s); mData.get(position).getComments().add(comment); ItemView itemView = mCachedViews.get(position); if (itemView != null && position == itemView.getPosition()) { itemView.addComment(); } et.setText(""); mCommentView.setVisibility(View.GONE); } } }); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
ItemView.java
package main.zhaizu.com.popupwindowdemo.ui;import android.content.Context;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.ColorDrawable;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.PopupWindow;import android.widget.TextView;import main.zhaizu.com.popupwindowdemo.R;import main.zhaizu.com.popupwindowdemo.model.Comment;import main.zhaizu.com.popupwindowdemo.model.Item;/** * Created by cmm on 15/10/31. */public class ItemView extends LinearLayout implements View.OnClickListener { private int mPosition; private Item mData; private ImageView mPortraitView; private TextView mUserNameView; private TextView mContentView; private TextView mCreatedAtView; private LinearLayout mCommentLayout; private View mMoreView; private PopupWindow mMorePopupWindow; private int mShowMorePopupWindowWidth; private int mShowMorePopupWindowHeight; private OnCommentListener mCommentListener; public ItemView(Context context) { super(context); } public ItemView(Context context, AttributeSet attrs) { super(context, attrs); } public interface OnCommentListener { void onComment(int position); } @Override protected void onFinishInflate() { super.onFinishInflate(); mPortraitView = (ImageView) findViewById(R.id.portrait); mUserNameView = (TextView) findViewById(R.id.nick_name); mContentView = (TextView) findViewById(R.id.content); mCreatedAtView = (TextView) findViewById(R.id.created_at); mCommentLayout = (LinearLayout) findViewById(R.id.comment_layout); mMoreView = findViewById(R.id.more_btn); } public void setPosition(int mPosition) { this.mPosition = mPosition; } public void setCommentListener(OnCommentListener l) { this.mCommentListener = l; } public void setData(Item data) { mData = data; mPortraitView.setImageResource(data.getPortraitId()); mUserNameView.setText(data.getNickName()); mContentView.setText(data.getContent()); updateComment(); mMoreView.setOnClickListener(this); } /** * 弹出点赞和评论框 * * @param moreBtnView */ private void showMore(View moreBtnView) { if (mMorePopupWindow == null) { LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); View content = li.inflate(R.layout.layout_more, null, false); mMorePopupWindow = new PopupWindow(content, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); mMorePopupWindow.setBackgroundDrawable(new BitmapDrawable()); mMorePopupWindow.setOutsideTouchable(true); mMorePopupWindow.setTouchable(true); content.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); mShowMorePopupWindowWidth = content.getMeasuredWidth(); mShowMorePopupWindowHeight = content.getMeasuredHeight(); View parent = mMorePopupWindow.getContentView(); TextView like = (TextView) parent.findViewById(R.id.like); TextView comment = (TextView) parent.findViewById(R.id.comment); comment.setOnClickListener(this); } if (mMorePopupWindow.isShowing()) { mMorePopupWindow.dismiss(); } else { int heightMoreBtnView = moreBtnView.getHeight(); mMorePopupWindow.showAsDropDown(moreBtnView, -mShowMorePopupWindowWidth, -(mShowMorePopupWindowHeight + heightMoreBtnView) / 2); } } private void updateComment() { if (mData.hasComment()) { mCommentLayout.removeAllViews(); mCommentLayout.setVisibility(View.VISIBLE); for (Comment c : mData.getComments()) { TextView t = new TextView(getContext()); t.setLayoutParams(new LinearLayout.LayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT))); t.setBackgroundColor(getResources().getColor(R.color.colorCommentLayoutBg)); t.setTextSize(16); t.setPadding(5, 2, 0, 3); t.setLineSpacing(3, (float) 1.5); t.setText(c.getComment()); mCommentLayout.addView(t); } } else { mCommentLayout.setVisibility(View.GONE); } } @Override public void onClick(View v) { int id = v.getId(); if (id == R.id.more_btn) { showMore(v); } else if (id == R.id.comment) { if (mCommentListener != null) { mCommentListener.onComment(mPosition); if (mMorePopupWindow != null && mMorePopupWindow.isShowing()) { mMorePopupWindow.dismiss(); } } } } public int getPosition() { return mPosition; } public void addComment() { updateComment(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
6. 感谢
本文中 listview 的布局改编自 http://download.csdn.net/download/weiyirong/6709151,感谢该作者的分享。
本文持续更新,转载请注明出处:http://blog.csdn.net/zhaizu/article/details/48103351
0 0