2018-11-29
在学习android自定义ViewGroup控件的过程中,随着学习的深入会很容易意识到一些问题
比如:ViewGroup可以让我通过重写onLayout方法来操作子view在ViewGroup中的布局 做出很多比如线性布局这样的自定义布局效果 但是有时候你会遇到
“不同的子view在布局中有不一样的布局需求”的情况
最常见的就是:FrameLayout和LinearLayout 在xml文件中添加子view时你会发现 FrameLayout的子view是不能添加Margin属性的 而LinearLayout却可以
这时你会发现 你的自定义ViewGroup里的子view也没有这个属性 这是什么原因?要知道 在android中要给一个view添加不同的属性是需要重写这个view带AttributeSet的构造方法的:
public TagGroup(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.TagGroup);
int targetColorInt = typedArray.getInt(R.styleable.TagGroup_targetColor,0);
if (targetColorInt != 0){
targetColor = context.getResources().getColor(targetColorInt);
}else targetColor = Color.WHITE;
typedArray.recycle();
}
要使用上面的方式获取 int targetColorInt 需要先在android的values文件夹下新建一个名为attrs的xml文件 :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TagGroup">
<attr name="targetColor" format="integer"/>
</declare-styleable>
</resources>
但是很明显 LinearLayout并没有使用这种方法 因为不管你添加什么view(包括你自定义的什么属性都没有的view)进去他们都会拥有Margin属性
要怎么才能让 “我的ViewGroup下的所有子view都拥有我希望它们拥有的属性呢?” 这就需要我们的generateLayoutParams方法了
首先 我们修改下之前在attrs文件中的代码:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TagGroup_Layout">
<attr name="layoutMargin" format="dimension"/>
</declare-styleable>
</resources>
然后 我们先在我们的自定义ViewGroup中定义一个静态的内部类:
public static class TagGroupLayoutParams extends MarginLayoutParams {
public float margin;
public TagGroupLayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.TagGroup_Layout);
margin = typedArray.getDimension(R.styleable.TagGroup_Layout_layoutMargin,0f);
typedArray.recycle();
}
public TagGroupLayoutParams(int width, int height) {
super(width, height);
}
public TagGroupLayoutParams(MarginLayoutParams source) {
super(source);
}
public TagGroupLayoutParams(LayoutParams source) {
super(source);
}
}
现在 在你的自定义ViewGroup中重写关键的generateLayoutParams方法:
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new TagGroupLayoutParams(getContext(), attrs);
}
现在你会发现 在你的xml中 你的自定义ViewGroup不但拥有了margin属性 还有了一个layoutMargin属性:
<com.example.godru.demo.TagGroup
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tag_group"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:myMargin="10dp"
android:layout_margin="10dp"
android:textSize="18sp"
android:text="@string/app_name"
/>
</com.example.godru.demo.TagGroup>
现在 你可以在任何地方通过获取到你的ViewGroup中的子view 然后调用他们的getLayoutParams()方法来获取TagGroupLayoutParams了 之后就可以使用你的自定义属性来布局你的控件了
终上 其实generateLayoutParams方法的作用其实就是定义你的控件下所有子控件所使用的layoutParams类 通过这种形式使你的控件可以按自己想要的方式和属性来操作它的子view 你甚至不需要关心子view本身 只要你重写过generateLayoutParams方法 他们就一定会使用你给的LayoutParams来修饰自己 你也必然可以通过getLayoutParams方法获取到 是一个高度解耦合的设计
注:自定义属性可能在androidStudio中会报红 说找不到目标属性 这是编译问题 不会影响运行 通常把as关掉再进就会好