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

项目地址:github.com/razerdp/Fri…

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

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


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

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

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


方案

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

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

我们可以在源码看到:

好的,扯得有点远,那这个方法用来干嘛呢?其实就是用来做一件事:让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看吧,为了篇幅,就不贴了,直接上图算了):

嗯,如您所见,我这里的构造是多层嵌套,是的,这会导致过度绘制。但为何我还要选择这么做呢(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);

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

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

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

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)分离出来,防止耦合度过高,同时完成我们的点击事件和后台交互。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值