Android开源控件viewbadger的原理及使用

viewbadger是github上的一个开源控件,能够以一个小徽章的样式附着在某个view上,通常用于显示未读消息数,典型的如微信、QQ、微博等,地址为:https://github.com/jgilfelt/android-viewbadger。下图为项目主页给出的样式图,可见还是能满足各种需求的。


viewbadger继承自textview,使用起来很简单,项目主页给出的Sample就四行:

View target = findViewById(R.id.target_view);
BadgeView badge = new BadgeView(this, target);
badge.setText("1");
badge.show();

看起来虽然简单,但用起来如果不小心也可能会出现一些意外,造成布局发生错误。我第一次使用时简单地把创建两个badgeview并设置target,却造成了这样的结果:

可见Item中的布局发生了错误,而底部bar的一项没显示出来。以下分别是listview中item的布局和底部bar的布局

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="64dp"
    android:clickable="true"
    android:id="@+id/layout_root"
    android:background="@drawable/item_normal_background"
    android:gravity="center_vertical">
        <ImageView
            android:id="@+id/iv_avatar"
            android:layout_width="56dp"
            android:layout_height="56dp"
            android:src="@drawable/display_picture_default"
            />

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_avatar"
        android:layout_marginTop="2dp"
        android:textSize="14sp"
        android:textColor="#000000"
        android:ellipsize="end"
        android:text="name"
        android:lines="1"/>
    <TextView
        android:id="@+id/tv_content"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/iv_avatar"
        android:layout_below="@id/tv_name"
        android:layout_marginTop="2dp"
        android:textSize="12sp"
        android:textColor="#7A7E83"
        android:ellipsize="end"
        android:text="content"
        android:lines="1" />
    <TextView
        android:id="@+id/tv_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="4dp"
        android:layout_marginRight="8dp"
        android:textSize="10sp"
        android:textColor="#7A7E83"
        android:ellipsize="end"
        android:text="time"
        android:lines="1" />

</RelativeLayout>
<span style="font-family: Arial, Helvetica, sans-serif;"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"</span>
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:orientation="horizontal">

    <com.example.lbf.imitationofwechat.views.TabItemView
        android:id="@+id/tab_item_chats"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        app:description="微信"
        app:iconNormal="@drawable/chats1"
        app:iconSelected="@drawable/chats2"
        android:padding="8dp"/>

    <com.example.lbf.imitationofwechat.views.TabItemView
        android:id="@+id/tab_item_contacts"
        android:layout_width="0dp "
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:padding="8dp"
        app:description="通讯录"
        app:iconNormal="@drawable/contacts1"
        app:iconSelected="@drawable/contacts2" />

    <com.example.lbf.imitationofwechat.views.TabItemView
        android:id="@+id/tab_item_discover"
        android:layout_width="0dp "
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:padding="8dp"
        app:description="发现"
        app:iconNormal="@drawable/discover1"
        app:iconSelected="@drawable/discover2" />

    <com.example.lbf.imitationofwechat.views.TabItemView
        android:id="@+id/tab_item_me"
        android:layout_width="0dp "
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:padding="8dp"
        app:description="我"
        app:iconNormal="@drawable/me1"
        app:iconSelected="@drawable/me2" />

</LinearLayout>

主布局分别使用了linearlayout和relativelayout,可以猜测使用了badgeview后对控件的分布造成了影响才导致这样的结果,那么就来看看的badgeview的源码,主要的代码如下:

private void applyTo(View target) {
    LayoutParams lp = target.getLayoutParams();
    ViewParent parent = target.getParent();
    FrameLayout container = new FrameLayout(this.context);
    if(target instanceof TabWidget) {
        target = ((TabWidget)target).getChildTabViewAt(this.targetTabIndex);
        this.target = target;
        ((ViewGroup)target).addView(container, new LayoutParams(-1, -1));
        this.setVisibility(8);
        container.addView(this);
    } else {
        ViewGroup group = (ViewGroup)parent;
        int index = group.indexOfChild(target);
        group.removeView(target);
        group.addView(container, index, lp);
        container.addView(target);
        this.setVisibility(8);
        container.addView(this);
        group.invalidate();
    }

}


思路很简单,首先把target从所在的viewgroup中移除,然后创建一个framelayout,把target和badgeview放入framelayout,最后再把framelayout放回原来的viewgroup中,接下来分析为什么会出现那样的结果。
一.  第一个layout中用的是relativelayout,头像的右边有两个textView,分别显示昵称和内容,其位置通过layout_toRightOf="@id/iv_avatar"来确定,因此是依赖于头像的imageview的。根据badgeview的原理,会为头像创建一个新的framelayout,此时头像与两个textView不再属于同一层级了,而layout_toRightOf只能作用在同一层级的view,因此失效了,导致两个textView位于relativelayout 的开头处。解决办法很简单,让两个textView的位置不要依赖于头像的imageview即可。
二.  第二个layout中用的是linearlayout,且四个子view均分其宽度。使用badgeview作用于其中一个item后,同样会创建一个新的framelayout,此framelayout的属性与原来的item相同,因此不会影响其他三个子view的位置,但是原来的子view就不显示了。这是因为原来的子view的宽度设为了0,在新的framelayout中weigh又不起作用,因此宽度为0,自然就不显示了。解决办法也很简单,将原来的四个子view的宽度全设为match_parent,或者为其设置一个最小宽度就可以了。

修改布局后再调整一些样式最后得到的结果如下,还是比较不错的。明白了原理以后,使用badgeview时就需要考虑一下为target新创建一个framelayout后是否会对原来的布局产生影响,这样就不会出错了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值