Android仿手机QQ空间动态评论,自动定位到输入框

手机QQ空间浏览好友动态时,可以直接对动态评论,点击某条评论,动态列表自动滚动,使输入框刚好在该评论下面,而不会覆盖住评论内容。如下图所示,

Android仿手机QQ空间动态评论,自动定位到输入框_第1张图片

首先要实现输入框刚好在输入面板上面,且动态列表不会被挤上去。可以使用对话框的形式,这样输入框不会影响原有的布局,弹出的对话框布局如下所示,点击EditText时,红色块的内容将位于输入法上面。在这里我把ScrollerView的背景设为透明。其实QQ空间的输入框也是以对话框的形式弹出,因为弹出对话框时,原本全屏的布局突然多了一条状态栏。

Android仿手机QQ空间动态评论,自动定位到输入框_第2张图片
接着就是要让点击的评论刚好在评论输入框上面,可以使用ListView.smoothScrollBy(distance, duration)让列表滚动到相应位置,但是难点是如何计算出滚动的距离distance。

如下图所示,我们主要计算弹出输入面板后的输入框和评论之间的距离(绿色线条的长度)。通过View.getLocationOnScreen()方法可以计算出view在屏幕上的坐标(x,y),那么列表滑动的距离distance = listview的y坐标 - 输入框的y坐标。

Android仿手机QQ空间动态评论,自动定位到输入框_第3张图片

/**
     * 弹出评论对话框
     */
    public static void inputComment(final Activity activity, final ListView listView,
                                    final View btnComment, final User receiver,
                                    final InputCommentListener listener) {

        final ArrayList commentList = (ArrayList) btnComment.getTag(KEY_COMMENT_SOURCE_COMMENT_LIST);

        String hint;
        if (receiver != null) {
            if (receiver.mId == MainActivity.sUser.mId) {
                hint = "我也说一句";
            } else {
                hint = "回复 " + receiver.mName;
            }
        } else {
            hint = "我也说一句";
        }
        // 获取评论的位置,不要在CommentDialogListener.onShow()中获取,onShow在输入法弹出后才调用,
        // 此时btnComment所属的父布局可能已经被ListView回收
        final int[] coord = new int[2];
        if (listView != null) {
            btnComment.getLocationOnScreen(coord);
        }

        //弹出评论对话框
        showInputComment(activity, hint, new CommentDialogListener() {
            @Override
            public void onClickPublish(final Dialog dialog, EditText input, final TextView btn) {
                final String content = input.getText().toString();
                if (content.trim().equals("")) {
                    Toast.makeText(activity, "评论不能为空", Toast.LENGTH_SHORT).show();
                    return;
                }
                btn.setClickable(false);
                final long receiverId = receiver == null ? -1 : receiver.mId;
                Comment comment = new Comment(MainActivity.sUser, content, receiver);
                commentList.add(comment);
                if (listener != null) {
                    listener.onCommitComment();
                }
                dialog.dismiss();
                Toast.makeText(activity, "评论成功", Toast.LENGTH_SHORT).show();
            }

            /**
             * onShow在输入法弹出后才调用
             * @param inputViewCoordinatesInScreen  输入框距离屏幕顶部(不包括状态栏)的位置[left,top]
             */
            @Override
            public void onShow(int[] inputViewCoordinatesInScreen) {
                if (listView != null) {
                    // 点击某条评论则这条评论刚好在输入框上面,点击评论按钮则输入框刚好挡住按钮
                    int span = btnComment.getId() == R.id.btn_input_comment ? 0 : btnComment.getHeight();
                    listView.smoothScrollBy(coord[1] + span - inputViewCoordinatesInScreen[1], 1000);
                }
            }

            @Override
            public void onDismiss() {

            }
        });

    }

最后要弄的就是评论中,评论者、接收者和评论内容用不同的颜色显示,且点击时有点击效果。这里可以通过下面的代码实现,

TextView.setText(Html.fromHtml(content, imageGettor, tagHandler))

自定义标签,通过自定义的标签解析类Html.TagHandler来响应不同标签的操作。这里我自定义了commentator、receiver、content标签,列入一条评论的字符串形式为“用户1 回复 用户2:评论内容”,点击content标签时对该评论的评论者进行回复。

/**
 * 显示评论的自定义Html标签解析器
 */
public class CustomTagHandler implements Html.TagHandler {
    //自定义标签
    public static final String TAG_COMMENTATOR = "commentator"; // 评论者
    public static final String TAG_RECEIVER = "receiver"; // 评论接收者,即对谁评论
    public static final String TAG_CONTENT = "content"; // 评论内容

    ...

    /**
     * 解析自定义标签
     */
    @Override
    public void handleTag(boolean opening, String tag, final Editable output, XMLReader xmlReader) {
        if (!tag.toLowerCase().equals(TAG_COMMENTATOR) && !tag.toLowerCase().equals(TAG_RECEIVER)
                && !tag.toLowerCase().equals(TAG_CONTENT)) {
            return;
        }
        if (opening) {  //开始标签
            // 记录标签内容的起始索引
            int mStart = output.length();
            if (tag.toLowerCase().equals(TAG_COMMENTATOR)) {
                mMaps.put(KEY_COMMENTATOR_START, mStart);
            } else if (tag.toLowerCase().equals(TAG_RECEIVER)) {
                mMaps.put(KEY_RECEIVER_START, mStart);
            } else if (tag.toLowerCase().equals(TAG_CONTENT)) {
                mMaps.put(KEY_CONTENT_START, mStart);
            }
        } else { // 结束标签
            int mEnd = output.length(); //标签内容的结束索引

            if (tag.toLowerCase().equals(TAG_COMMENTATOR)) {
                int mStart = mMaps.get(KEY_COMMENTATOR_START);
                output.setSpan(new TextAppearanceSpan(mContext, R.style.Comment),
                        mStart, mEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                output.setSpan(mCommentatorSpan, mStart, mEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

            } else if (tag.toLowerCase().equals(TAG_RECEIVER)) {
                int mStart = mMaps.get(KEY_RECEIVER_START);
                output.setSpan(new TextAppearanceSpan(mContext, R.style.Comment),
                        mStart, mEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                output.setSpan(mReceiverSpan, mStart, mEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

            } else if (tag.toLowerCase().equals(TAG_CONTENT)) {
                int mStart = mMaps.get(KEY_CONTENT_START);
                output.setSpan(new TextAppearanceSpan(mContext, R.style.Comment),
                        mStart, mEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
                output.setSpan(mContentSpan, mStart, mEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
    }

   ...
}

在ListView中,因为Item里面的子View使用了ClickableSpan,导致ListView的OnItemClickListener失效,解决的方法可以在getView中加入下列代码,阻止ListView里面的子View拦截焦点。

public View getView(int position, View convertView, ViewGroup parent) {
	if (convertView != null) {
		    //防止ListView的OnItemClick与item里面子view的点击发生冲突
	((ViewGroup) convertView).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
	}
}

设置点击的文字背景色:



难点都已经解决了,最后实现的效果如下:



完整demo:https://github.com/1993hzw/QZoneComment


由于本人的毕业论文的题目为基于android的QQ空间模拟系统,所以对android版的QQ空间研究了不少,山寨了其核心功能。之后有机会继续跟大家分享经验,谢谢支持。


你可能感兴趣的:(android)