一起撸个朋友圈吧(step5) - 控件篇(评论popup上)

项目地址:https://github.com/razerdp/FriendCircle
一起撸个朋友圈吧这是本文所处文集,所有更新都会在这个文集里面哦,欢迎关注

上篇链接:http://www.jianshu.com/p/280058de01bd
下篇链接:http://www.jianshu.com/p/deda1849a084

嗯。。。今天要做的效果是这样滴(点击事件暂时未做)- -

preview

在本篇开始之前,我觉得您有必要看看basepopup那篇文章(点我查看),因为那篇文章就是为本篇所准备的,本篇所使用的popup就是基于那篇文章所说,为了篇幅,所以就不多解释了。

ps:羽翼君我其实并不太喜欢贴太多代码的,一般而言一篇文章我是不会贴过多的代码,因为如果满屏的代码带来的是阅读的疲劳,所以一般而言,如果实现流程比较长,代码量较多,我都会分上下篇来讲述。给您带来断断续续的阅读体验,这里在下说声抱歉了。


方案

按照国际惯例,咱们还是先聊聊如何实现。众所周知,popupWindow有个showAtLocation,很明显,我们朋友圈的评论弹出的popup也需要这个方法,而问题就是在于,我们该如何得到这三个小点点的具体位置呢?

这里就不得不说到这个方法:getLocationOnScreen,当然,还有一个相似的getLocationInWindow这两者的区别在于前者获取当前view相对于整个屏幕的xy pos,后者则是当前view相对于的父窗口的xy pos。不过,两者调用的,其实还是getLocationInWindow

我们可以在源码看到:

getLocationOnScreen

好的,扯得有点远,那这个方法用来干嘛呢?其实就是用来做一件事:让popup正确的显示在某个小点点的左边。也就是效果图的那个效果。


开工(上篇仅完成重要部分代码的实现,下篇完成点击事件,后台处理等

OK,前戏搞完,开始进入XXOO的阶段。

step 1

首先,我们继承BasePoup,是的,如果您看过basepopup那篇文章,就知道我们需要实现4个方法:

public class CommentPopup extends BasePopupWindow {

    public CommentPopup(Activity context) {
        super(context, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    @Override
    protected Animation getShowAnimation() {
        return null;
    }

    @Override
    protected View getClickToDismissView() {
        return null;
    }

    @Override
    public View getPopupView() {
        return null;
    }

    @Override
    public View getAnimaView() {
        return null;
    }
}

为了更方便使用,我们直接调用父类的另一个构造方法,传入的是wrap_contnet

(*注:因为默认是match_parent,但我们并不需要我们的评论popup占满整个屏幕,所以构造器就传入wrap_content,当然,对于外部调用者来说,他仅仅需要传入context)。

step 2

接下来我们开始进行布局。首先去drawable画出我们的背景:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    
    <solid android:color="@color/comment_popup_bg"/>
    <corners android:radius="4dp"/>

</shape>

(其中comment_popup_bg色值为:#4c5154)
然后新建布局popup_comment(详情去github看吧,为了篇幅,就不贴了,直接上图算了):

comment_popup_bg

嗯,如您所见,我这里的构造是多层嵌套,是的,这会导致过度绘制。但为何我还要选择这么做呢(ps:如果您有更好的方法,希望能在评论区贴一下或者在github提交pr)

  • 其一是因为我们的图标在文字的左边,虽然可以选择只用一个textview加drawableLeft
    但是如您所见,我们的textview的宽度是平分整个父view的,所以如果选择单一的textview,就会导致我们的图标与文字的距离十分的远。

  • 其二,考虑到我们的popup并非常驻于界面,所以此处多层嵌套影响并不大。

另外,需要注意的是我们需要给viewgroup给定一个android:clipChildren="false",这么做是为了不让viewgroup阻断我们的动画展示。

ok,布局完成。

step 3

布局完成后我们开始实现java代码:

首先为了方便管理参数,同时也为了防止服务器经常性修改JSON下发参数,我们新建一个工具类来管理:

/**
 * Created by 大灯泡 on 2016/3/6.
 * 各种设定值
 */
public class CommonValue {
    // 点赞状态
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({NOT_PRAISE,HAS_PRAISE})
    public @interface PraiseState{}

    public static final int NOT_PRAISE = 0;
    public static final int HAS_PRAISE = 1;
}

在我们这个系列里,点过赞的状态下发的是1,没有则是下发的0。同时我们定义一个注解,用于限定传参。

接下来到我们的CommentPopup,定义各种变量:

    // 点赞的图标
    private ImageView mLikeView;
    private TextView mLikeText;
    // 点赞/评论的layout
    private RelativeLayout mLikeClikcLayout;
    private RelativeLayout mCommentClickLayout;
    // 动态信息
    private DynamicInfo mDynamicInfo;
    // 位置数组
    private int[] viewLocation;
    // handler
    private WeakHandler handler;

其中WeakHandler是一个弱引用的静态内部类:

  static class WeakHandler extends Handler{
        private final WeakReference<Context> contenxt;

        public WeakHandler(Context contenxt) {
            this.contenxt = new WeakReference<Context>(contenxt);
        }
    }

至于这个handler用来干嘛。。。。嗯,备用 - -

接下来需要定义一下我们的show方法:

  @Override
    public void showPopupWindow(View v) {
        try {
            //得到v的位置
            v.getLocationOnScreen(viewLocation);
            //展示位置:
            //参照点为window的右上角,偏移值为:x方向距离参照view的一定倍数距离
            //垂直方向自身减去popup自身高度的一半(确保在中间)
            mPopupWindow.showAtLocation(v, Gravity.RIGHT | Gravity.TOP, (int) (v.getWidth() * 1.8),
                    viewLocation[1] - UIHelper.dipToPx(mContext, 10f));

            if (getShowAnimation() != null && getAnimaView() != null) {
                getAnimaView().startAnimation(getShowAnimation());
            }
        } catch (Exception e) {
            Log.w("error", "error");
        }
    }

这里需要说说showAtLocation这个方法。

public void showAtLocation(View parent, int gravity, int x, int y) 

这个方法有一定的误导性,一开始我还以为gravity是相对于显示的view来说的,打个比方,假如我showAtLocation(view,Gravity.RIGHT,0,0);按照我一开始的说法,我会以为这个popup在view的右边显示。

结果并不是。你会发现,popup在屏幕右边正中间显示。

百思不得其解,百度过后得到的答案不太清晰,只好去看源码了。

点进这个方法的源码,我们看到调用了showAtLocation(parent.getWindowToken(), gravity, x, y);

showAtLocation

再追溯下去,可以看到我们的Gravity的赋值

showAtLocation

也就是说,在show的时候会创建一个LayoutParams,并把gravity赋值(如果gravity有值的话),追溯到这里,有没有感觉像平时写viewgroup时,给的gravity。

我们继续追溯下去,在invokePopup方法里面,我们的layoutparams会传递下去

invokePopup

ok,看到我们的addView没有,我们的view将会被add到当前window,也就是说我们的Gravity的参考点,其实是整个window,而与我们的view无关。

那么我们要把popup显示到正确位置,就需要后面的两个参数了。

通过方法看出,showAtLocation后面两个参数正是x,y的偏移量。

所以我们的取值方案如下:

  1. 得到view的位置(getLocationOnScreen,得到view左上角原点坐标位置)
  2. Gravity取window的右上角(right|top)
  3. x方向偏移量取view的x同时乘以一定倍数(使popup的位置在view的左边)
  4. y方向偏移量去view的y并减去自身的一半高度。
  5. showPopup

代码在上面。

在展示完成后,最后需要根据我们的点赞状态(是否点赞)来进行对应的设置:

  public void setDynamicInfo(DynamicInfo info) {
        if (info == null) return;
        if (info.praiseState == CommonValue.HAS_PRAISE) {
            mLikeText.setText("取消");
        }
        else {
            mLikeText.setText("赞  ");
        }
    }

然后回到我们的BaseItemDelegate执行如下操作(初始化popup略过):

   @Override
    public void onClick(View v) {
        switch (v.getId()){
            // 评论按钮
            case R.id.comment_button:
                if (mInfo==null)return;
                mCommentPopup.setDynamicInfo(mInfo.dynamicInfo);
                mCommentPopup.showPopupWindow(commentImage);
                break;
            default:
                break;
        }
    }

本篇完。

下一篇将会做一个比较大的改动,我们需要将事件处理从viewholder(即BaseItemDelegate)分离出来,防止耦合度过高,同时完成我们的点击事件和后台交互。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容