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();
}
}
一. 第一个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后是否会对原来的布局产生影响,这样就不会出错了。