读取自定义属性
通常读取自定义控件的自定义属性是在自定义控件的构造函数中用context的obtainStyledAttributes方法获取TpyeArray对象,通过TpyeArray对象获取个自定义属性,最后别忘了recycle()。
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyViewGroup" >
<attr name="x_in" format="float"/>
<attr name="y_in" format="float"/>
</declare-styleable>
</resources>
自定义控件public MyViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array=context.obtainStyledAttributes(attrs,R.styleable.MyViewGroup);
x_in=array.getFloat(R.styleable.MyViewGroup_x_in,0);
y_in=array.getFloat(R.styleable.MyViewGroup_y_in,0);
array.recycle();
}
当系统控件中带有自定义属性如何读取?
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
vg:x_in="0.2"
vg:y_in="0.3"
/>
方法一:利用LayoutParams
当自定义控件是ViewGroup的子控件时且带有自定义属性的系统控件是其内,如下布局
<com.znvoid.blogs.MyViewGroup
android:id="@+id/myview"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
vg:x_in="0.2"
vg:y_in="0.3"
/>
</com.znvoid.blogs.MyViewGroup>
首先了解下generateLayoutParams()方法,为父容器生成 子view 的布局LayoutParams在Layoutinflater.rInflate()中被调用
public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); }
如果一个View想要被添加到这个容器中,这个view可以调用此方法生成和容器类匹配的布局LayoutParams;
那么可以在自定义控件中重写generateLayoutParams()方法生成自定义的LayoutParams类对象从而达到读取自定义属性。
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MyLayoutParams(getContext(), attrs);
}
class MyLayoutParams extends LayoutParams{
public float x_in;
public float y_in;
public MyLayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray array=c.obtainStyledAttributes(attrs,R.styleable.MyViewGroup);
x_in=array.getFloat(R.styleable.MyViewGroup_x_in,0);
y_in=array.getFloat(R.styleable.MyViewGroup_y_in,0);
//System.out.println("x_in"+x_in);
array.recycle();
}
}
方法二 LayoutInflater
布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:x_in="1.2"
app:y_in="2.3"/>
</RelativeLayout>
先看LayoutInflater.inflate()源码
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) { return inflate(resource, root, root != null); }
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { final Resources res = getContext().getResources(); if (DEBUG) { Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" (" + Integer.toHexString(resource) + ")"); } final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); //省略代码...... // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, inflaterContext, attrs);//省略代码......
}}
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) {//省略代码......
try { View view; if (mFactory2 != null) { view = mFactory2.onCreateView(parent, name, context, attrs); } else if (mFactory != null) { view = mFactory.onCreateView(name, context, attrs); } else { view = null; } if (view == null && mPrivateFactory != null) { view = mPrivateFactory.onCreateView(parent, name, context, attrs); }//省略代码......
}
通过源码看出LayoutInflater.inflate()方法会调用createViewFromTag()---->mFactory.onCreateView(name,context,attrs)
只要重写Factory.onCreateView()就能得到控件的AttributeSet属性对象
*
* 自定义布局加载器
*/
public class MyLayoutInflater extends LayoutInflater {
public MyLayoutInflater( Context newContext) {
super( newContext);
setFactory(new MyFatory());
}
public MyLayoutInflater(LayoutInflater original, Context newContext) {
super(original, newContext);
setFactory2(new MyFactory2());
}
@Override
public LayoutInflater cloneInContext(Context newContext) {
return new MyLayoutInflater(this, newContext);
}
public class MyFatory implements LayoutInflater.Factory {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return doCreateView(name,context,attrs);
}
//
}
public View doCreateView(String name, Context context, AttributeSet attrs) {
View view = null;
if (name.contains(".")) {//自定义控件生成
view = doCreateViewWithPackage(name, null, context, attrs);
} else {//系统控件的生成
if ((view = doCreateViewWithPackage(name, "android.view.", context, attrs)) == null)
view = doCreateViewWithPackage(name, "android.widget.", context, attrs);
}
if (view != null) {//自定义属性的读取
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyViewGroup);
float x_in = array.getFloat(R.styleable.MyViewGroup_x_in, 0);
float y_in = array.getFloat(R.styleable.MyViewGroup_y_in, 0);
System.out.println("x_in的值:" + x_in);
array.recycle();
}
return view;
}
private View doCreateViewWithPackage(String name, String prefix, Context context, AttributeSet attrs) {//根据控件名创建控件对象
try {
return MyLayoutInflater.this.createView(name, prefix, attrs);
} catch (ClassNotFoundException e) {
//e.printStackTrace();
}
return null;
}
class MyFactory2 implements Factory2 {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
return onCreateView(name,context,attrs);
}
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return doCreateView(name,context,attrs);
}
}
}
用自定义布局加载器去加载带有自定义属性的系统控件的布局就能读取其中的自定义属性