分享

Android 仿微信点赞和评论弹出框

 jnstyle 2016-03-28

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

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

1. 微信弹出框

微信朋友圈的点赞和评论功能,有2个组成部分:

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

6. 感谢

本文中 listview 的布局改编自 http://download.csdn.net/download/weiyirong/6709151,感谢该作者的分享。

本文持续更新,转载请注明出处:http://blog.csdn.net/zhaizu/article/details/48103351

这是本人做的关于眼睛健康的微信公众号,专注 IT 人,只出精品图文,更有护眼知识闯关游戏,欢迎关注。



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约