前言:
我们有时候,需要在已经设计好的界面上的某一个View上,在事后添加一些view. 如一个消息到达的时候,在切换到消息的那个空间上面,显示一个消息数量的图标。
在开源的项目 https://github.com/jgilfelt/android-viewbadger 中,实现了这样的效果。见下图
在上图的Button 中,上面的红色的提示的图标不是在布局中添加的,而是通过BadgeView 这个类实现的通过原始的控件,动态的添加到这个界面上去的。
BadgeView 源码原理分析:
部分源码的分析载录.
public BadgeView(Context context) {
this(context, (AttributeSet) null, android.R.attr.textViewStyle);
}
public BadgeView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
}
/**
* Constructor -
*
* create a new BadgeView instance attached to a target {@link android.view.View}.
*
* @param context context for this view.
* @param target the View to attach the badge to.
*/
public BadgeView(Context context, View target) {
this(context, null, android.R.attr.textViewStyle, target, 0);
}
/**
* Constructor -
*
* create a new BadgeView instance attached to a target {@link android.widget.TabWidget}
* tab at a given index.
*
* @param context context for this view.
* @param target the TabWidget to attach the badge to.
* @param index the position of the tab within the target.
*/
public BadgeView(Context context, TabWidget target, int index) {
this(context, null, android.R.attr.textViewStyle, target, index);
}
public BadgeView(Context context, AttributeSet attrs, int defStyle) {
this(context, attrs, defStyle, null, 0);
}
public BadgeView(Context context, AttributeSet attrs, int defStyle, View target, int tabIndex) {
super(context, attrs, defStyle);
init(context, target, tabIndex);
}
private void init(Context context, View target, int tabIndex) {
this.context = context;
this.target = target;
this.targetTabIndex = tabIndex;
// apply defaults
badgePosition = DEFAULT_POSITION;
badgeMarginH = dipToPixels(DEFAULT_MARGIN_DIP);
badgeMarginV = badgeMarginH;
badgeColor = DEFAULT_BADGE_COLOR;
setTypeface(Typeface.DEFAULT_BOLD);
int paddingPixels = dipToPixels(DEFAULT_LR_PADDING_DIP);
setPadding(paddingPixels, 0, paddingPixels, 0);
setTextColor(DEFAULT_TEXT_COLOR);
fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new DecelerateInterpolator());
fadeIn.setDuration(200);
fadeOut = new AlphaAnimation(1, 0);
fadeOut.setInterpolator(new AccelerateInterpolator());
fadeOut.setDuration(200);
isShown = false;
if (this.target != null) {
applyTo(this.target);
} else {
show();
}
}
private void applyTo(View target) {
// 1、获取目标源码的布局
LayoutParams lp = target.getLayoutParams();
ViewParent parent = target.getParent();
/**
* 新建个容器 存放 原来的target 和 即将添加的 通知信息的TextView
*/
FrameLayout container = new FrameLayout(context);
if (target instanceof TabWidget) {
// set target to the relevant tab child container
target = ((TabWidget) target).getChildTabViewAt(targetTabIndex);
this.target = target;
((ViewGroup) target).addView(container,
new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
this.setVisibility(View.GONE);
container.addView(this);
} else {
// TODO verify that parent is indeed a ViewGroup
//获取target View的 所在的容器
ViewGroup group = (ViewGroup) parent;
//获取target View的 所在的容器中的位置
int index = group.indexOfChild(target);
//target View的 所在的容器 将 target View 移除
group.removeView(target);
// 将新建的 即将包好 target View 和 badge view(在此处是,这个 通知的TextView) 所在的容器,添加到原来target view 所在的位置
group.addView(container, index, lp);
/**
* 在自己新建的容器中间 添加target view 和自己 新定义的view.
*/
container.addView(target);
this.setVisibility(View.GONE);
container.addView(this);
// 界面刷新
group.invalidate();
}
}
自定义的例子:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
final TextView text = (TextView) findViewById(R.id.test);
Button button = (Button) findViewById(R.id.button);
final BadgeView badgeView=new BadgeView(this, text);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
badgeView.toggle();
}
});
}
}
public class BadgeView {
private Context mContext;
private View targeView;
TextView textView;
public BadgeView(Context mContext, View targeView) {
super();
this.mContext = mContext;
this.targeView = targeView;
// 1、获取目标源码的布局
LayoutParams lParams = targeView.getLayoutParams();
ViewParent targetViewParent = targeView.getParent();
/**
* 新建个容器 存放 原来的target 和 即将添加的 通知信息的TextView
*/
FrameLayout container=new FrameLayout(mContext);
//获取target View的 所在的容器
ViewGroup targetViewParentGroup=(ViewGroup) targetViewParent;
//获取target View的 所在的容器中的位置
int index=targetViewParentGroup.indexOfChild(targeView);
//target View的 所在的容器 将 target View 移除
targetViewParentGroup.removeView(targeView);
// 将新建的 即将包好 target View 和 badge view(在此处是,这个 通知的TextView) 所在的容器,添加到原来target view 所在的位置
targetViewParentGroup.addView(container, index, lParams);
/**
* 在自己新建的容器中间 添加target view 和自己 新定义的view.
*/
textView=new TextView(mContext);
textView.setText("sss");
textView.setTextColor(Color.RED);
textView.setVisibility(View.INVISIBLE);
container.addView(targeView);
container.addView(textView);
// 界面刷新
targetViewParentGroup.invalidate();
}
public void show() {
textView.setVisibility(View.VISIBLE);
}
public void hide() {
textView.setVisibility(View.INVISIBLE);
}
boolean isShow=false;
public void toggle(){
if (isShow) {
hide();
isShow=false;
}else {
show();
isShow=true;
}
}
}
布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.myviewbadger.MainActivity$PlaceholderFragment" >
<TextView
android:id="@+id/test"
android:layout_width="300dp"
android:layout_height="200dp"
android:background="@android:color/darker_gray"
android:text="@string/hello_world" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="ss" />
</RelativeLayout>
源码托管在:https://github.com/dblackde/MyViewBadger
参考:
https://github.com/jgilfelt/android-viewbadger