Android 仿微信朋友圈点赞和评论弹出框功能

贡献/下载源码:https://github.com/mmlovesyy/PopupWindowDemo

本文简单模仿微信朋友圈的点赞和评论弹出框,布局等细节请忽略,着重实现弹出框、发评论,及弹出位置的控制。

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);
}
// build data
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;
}
// custom adapter
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)) {
// update model
Comment comment = new Comment(s);
mData.get(position).getComments().add(comment);
// update view maybe
ItemView itemView = mCachedViews.get(position);
if (itemView != null && position == itemView.getPosition()) {
itemView.addComment();
}
et.setText("");
mCommentView.setVisibility(View.GONE);
}
}
});
}
}
}

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();
}
}

以上所述是小编给大家介绍的Android 仿微信朋友圈点赞和评论弹出框功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

时间: 2016-11-12

Android中自定义PopupWindow实现弹出框并带有动画效果

使用PopupWindow来实现弹出框,并且带有动画效果 首先自定义PopupWindow public class LostPopupWindow extends PopupWindow { public Lost lost; public void onLost(Lost lost){ this.lost = lost; } private View conentView; public View getConentView() { return conentView; } public L

Android 多种简单的弹出框样式设置代码

简介 这是一个基于AlertDialog和Dialog这两个类封装的多种弹出框样式,其中提供各种简单样式的弹出框使用说明.同时也可自定义弹出框. 项目地址:http://www.github.com/jjdxmashl/jjdxm_dialogui 特性 1.使用链式开发代码简洁明了 2.所有的弹出框样式都在DialogUIUtils这个类中完成,方便查阅方法 3.可以自定义弹出框字体样式 4.简单的类似加载框的样式可以支持两种主题更改默认白色和灰色 截图 demo下载 demo apk下载 D

Android 自定义弹出框实现代码

废话不多说了,直接给大家上关键代码了. - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [self showAlertView:@"11111"]; } //自定义弹出框 -(void)showAlertView:(NSString *)strTipText { UIView *showView=[[UIView alloc]init]; [sho

Android实现可输入数据的弹出框

之前一篇文章,介绍了如何定义从屏幕底部弹出PopupWindow即<Android Animation实战之屏幕底部弹出PopupWindow>,写完之后,突然想起之前写过自定义内容显示的弹出框,就随手写了两个实例,分享出来: 第一种实现方式:继承Dialog  1.1 线定义弹出框要显示的内容:create_user_dialog.xml <?xml version="1.0" encoding="utf-8"?> <LinearLa

高仿IOS的Android弹出框

先看一下效果图,不过这是网上的图片. 效果不错,就借此拿来与大伙分享分享. github源码地址:https://github.com/saiwu-bigkoo/Android-AlertView. 1.怎么用:添加依赖. compile 'com.bigkoo:alertview:1.0.3' 2.实例demo(大家可以根据需要来选择自己需要的框框). package com.example.my.androidalertview; import android.app.Activity; i

android中ProgressDialog与ProgressBar的使用详解

一 .ProgressDialogProgressDialog与ProgressBar在UI中动态显示一个加载图标显示程序运行状态.ProgressDialog是继承自Android.app.ProgressDialog所设计的互动对话窗口,使用时,必须新建ProgressDialog对象,在运行时会弹出"对话框"作为提醒,此时应用程序后台失去焦点(即此时无法对UI组件进行操作),直到进程结束后,才会将控制权交给应用程序,如果在Activity当中不希望后台失焦,又希望提示User有某

Android使用Dialog风格弹出框的Activity

在Android中经常会遇到需要使用Dialog风格弹出框的activity,首先我们可能会首先想到的是在XML布局文件中设置android:layout_height="wrap_content"属性,让activity的高度自适应,显然这还不行,我们还需要为其DialogActivity设置自定义一个样式 <style name="dialogstyle"> <!--设置dialog的背景--> <item name="a

Android编程实现仿QQ发表说说,上传照片及弹出框效果【附demo源码下载】

本文实例讲述了Android编程实现仿QQ发表说说,上传照片及弹出框效果.分享给大家供大家参考,具体如下: 代码很简单,主要就是几个动画而已,图标什么的就随便找了几个,效果图:   动画说明: 1.点击右上角按钮,菜单从顶部下拉弹出,同时背景变暗; 2.再次点击右上角按钮,点击返回键,或者点击空白区域(也就是变暗的部分),菜单向上收回; 3.点击菜单上的按钮响应事件,同时菜单收回(效果同2) 重要说明:动画结束后必须clearAnimation,否则隐藏状态的view依然能响应点击事件 主体代码

Android自定义弹出框dialog效果

项目要用到弹出框,还要和苹果的样式一样(Android真是没地位),所以就自己定义了一个,不是很像(主要是没图),但是也还可以. 废话不多说了,直接上代码 1.先看布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_wi

android控件封装 自己封装的dialog控件

自定义dialog肯定是用的很多了但是感觉每次做都是很乱 单纯完成任务而已,现在封装了一下 以后用到直接copy 先上图: 主activity 复制代码 代码如下: package com.su.testcustomdialog; import com.su.testcustomdialog.MyDialog.Dialogcallback; import android.app.Activity; import android.os.Bundle; import android.view.Vie

Android仿饿了么加入购物车旋转控件自带闪转腾挪动画的按钮效果(实例详解)

概述 在上文,酷炫Path动画已经预告了,今天给大家带来的是利用 纯自定义View,实现的仿饿了么加入购物车控件,自带闪转腾挪动画的按钮. 效果图如下: 图1 项目中使用的效果,考虑到了View的回收复用, 并且可以看到在RecyclerView中使用,切换LayoutManager也是没有问题的, 图2 Demo效果,测试各种属性值 注意,本控件非继承自ViewGroup,而是纯自定义View实现.理由如下: 1 减少布局层级,从而提高性能 2 文字和图形纯draw,用到什么draw什么,没有

Android自定义控件之创建可复用的组合控件

前面已学习了一种自定义控件的实现,是Andriod 自定义控件之音频条,还没学习的同学可以学习下,学习了的同学也要去温习下,一定要自己完全的掌握了,再继续学习,贪多嚼不烂可不是好的学习方法,我们争取学习了一种技术就会一种技术,而且不光看了就算了,最好的方法就是看完我自己再练习下,再扩展下,在原来的基础上在添加一些东西,比如,增加一些功能实现等等. 今天我们打算学习下另外一种自定义控件,就是创建可重复使用的组合控件,那么问题来了: 什么是可重复使用? 就是在应用中,可以在多个地方共同使用一套代码.

Android开发之无痕过渡下拉刷新控件的实现思路详解

相信大家已经对下拉刷新熟悉得不能再熟悉了,市面上的下拉刷新琳琅满目,然而有很多在我看来略有缺陷,接下来我将说明一下存在的缺陷问题,然后提供一种思路来解决这一缺陷,废话不多说!往下看嘞! 1.市面一些下拉刷新控件普遍缺陷演示 以直播吧APP为例: 第1种情况: 滑动控件在初始的0位置时,手势往下滑动然后再往上滑动,可以看到滑动到初始位置时滑动控件不能滑动. 原因: 下拉刷新控件响应了触摸事件,后续的一系列事件都由它来处理,当滑动控件到顶端的时候,滑动事件都被下拉刷新控件消费掉了,传递不到它的子控件

Android下拉刷新上拉加载控件(适用于所有View)

前面写过一篇关于下拉刷新控件的文章下拉刷新控件终结者:PullToRefreshLayout,后来看到好多人还有上拉加载更多的需求,于是就在前面下拉刷新控件的基础上进行了改进,加了上拉加载的功能.不仅如此,我已经把它改成了对所有View都通用!可以随心所欲使用这两个功能~~ 我做了一个大集合的demo,实现了ListView.GridView.ExpandableListView.ScrollView.WebView.ImageView.TextView的下拉刷新和上拉加载.后面会提供demo的

jQuery UI Dialog控件中的表单无法正常提交的解决方法

最近使用jQuery UI的Dialog控件时发现如果在此控件放置表单,则所有表单均无法正常提交,具体表现为: 1.提交按钮失效,点击后无任何反应. 2.即便是使用其它手段使页面产生提交,服务器端也无法取到Dialog中的表单数据. 研究了页面源码后发现,jQuery UI Dialog控件初始化时动态生成的HTML元素被添加到页面的尾部.form元素的后面,而原始的Dialog模板部分(其内包含表单元素)也被移到了 动态生成的HTML元素内.也就是说,原先在form内的表单在Dialog初始化

Android 中Volley二次封装并实现网络请求缓存

Android 中Volley二次封装并实现网络请求缓存 Android目前很多同学使用Volley请求网络数据,但是Volley没有对请求过得数据进行缓存,因此需要我们自己手动缓存. 一下就是我的一种思路,仅供参考 具体使用方法为: HashMap<String,String> params = new HashMap<>(); params.put("id", "1"); params.put("user", &quo

android实现图片验证码方法解析(自绘控件)

自绘控件的内容都是自己绘制出来的 大致流程如下: 1.定义一个类继承view 1.使用TypedArray初始化属性集合     在view的构造方法中 有一个AttributeSet的参数 很明显是用来保存控件属性信息的 我们也的确可以通过循环然后用键值对的方式获取信息 而TypedArray是用来简化我们的工作的 2.重写onMeasure 测量控件大小 3.重写onDraw 绘制控件 2.根据需求在attrs文件中自定义属性 declare-styleable 声明自定义属性可以自定义一个

android编程实现图片库的封装方法

本文实例讲述了android编程实现图片库的封装方法.分享给大家供大家参考,具体如下: 大家在做安卓应用的时候 经常要从网络中获取图片 都是通过URL去获取 可是如果本地有图片数据 从本地获取数据不更加快一些 自己在工作中遇到这个问题 所以采用了一个URL和本地图片的一个映射关系 先从本地区获取 假如本地没有再从网络中获取 本方法考虑到多线程问题 欢迎大家一起共同探讨! public class PictureLibrary { /* * 图片库的操作 */ File file; URL url

Android RecycleView添加head配置封装的实例

Android RecycleView添加head配置封装的实例 这个是把RecycleView的适配器给封装了,直接调用就可以了,还添加了可以添加head头部功能,很赞的,今天记下来,下次直接用 实例代码: package com.wwl.android; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.suppor

Android编程获取屏幕宽高与获取控件宽高的方法

本文实例讲述了Android编程获取屏幕宽高与获取控件宽高的方法.分享给大家供大家参考,具体如下: 获取屏幕宽高 // 获取屏幕宽高(方法1) int screenWidth = getWindowManager().getDefaultDisplay().getWidth(); // 屏幕宽(像素,如:480px) int screenHeight = getWindowManager().getDefaultDisplay().getHeight(); // 屏幕高(像素,如:800p) L