Android自定义控件之自定义属性解析

Android 中使用自定义属性的一般步骤:

  1. 定义declare-styleable,添加attr
  2. 使用TypedArray获取自定义属性
  3. 设置到View上

Sample

注意:

  • attr可以单独定义,不放在styleable中
  • 可以使用android内置attr,而不用声明format
  • 可以不用TypedArray来获取

attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="test">
        <attr name="size" format="dimension" />
        <attr name="android:textColor" />
    </declare-styleable>
</resources>

MyView:

/**
 * Created by bobomee on 16/1/25.
 */
public class MyView extends View {

    String T = this.getClass().getSimpleName();

    private float mSize;
    private int mBackGround;
    private Paint mPaint;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        handleAttrs(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        handleAttrs(context, attrs, defStyleAttr);
    }

    private void handleAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
        //Retrieve styles attributes
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.test, defStyleAttr, 0);

        int defaultSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, context.getResources().getDisplayMetrics());
        mSize = ta.getDimension(R.styleable.test_size, defaultSize);
        mBackGround = ta.getColor(R.styleable.test_android_textColor, Color.WHITE);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(mBackGround);

        Log.d(T, "mSize = " + mSize + " , mBackGround = " + mBackGround);
        ta.recycle();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(0,0,mSize,mPaint);
    }
}

activity.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:background="@color/colorAccent">

    <com.bobomee.android.attrstest.MyView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="@color/colorPrimary"
        android:textColor ="#00ffab"
        app:size="50dp" />
</RelativeLayout>

其中declare-styleable不是自定义View
android:textColor是内置属性,没有指定format

运行效果如下:
效果图

AttributeSet&&TypedArray

在自定义View中,除了第一个构造函数,都包含一个参数AttributeSet(View属性),可以通过它来获取属性,而不用TypedArray

  private void handleAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
        //Retrieve styles attributes
//        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.test, defStyleAttr, 0);
//
//        int defaultSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, context.getResources().getDisplayMetrics());
//        mSize = a.getDimension(R.styleable.test_size, defaultSize);
//
//        mBackGround = a.getColor(R.styleable.test_android_textColor, Color.WHITE);
//
//        mPaint = new Paint();
//        mPaint.setAntiAlias(true);
//        mPaint.setColor(mBackGround);

//        Log.d(T, "mSize = " + mSize + " , mBackGround = " + mBackGround);

        int count = attrs.getAttributeCount();
        for (int i = 0; i < count; i++) {
            String attrName = attrs.getAttributeName(i);
            String attrVal = attrs.getAttributeValue(i);
            Log.e(T, "attrName = " + attrName + " , attrVal = " + attrVal);
        }
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
//        canvas.drawCircle(0,0,mSize,mPaint);
    }

logcat信息:

01-25 23:45:04.989 16613-16613/com.bobomee.android.attrstest E/MyView: attrName = textColor , attrVal = #ff00ffab
01-25 23:45:04.989 16613-16613/com.bobomee.android.attrstest E/MyView: attrName = background , attrVal = @2131427347
01-25 23:45:04.989 16613-16613/com.bobomee.android.attrstest E/MyView: attrName = layout_width , attrVal = 200.0dip
01-25 23:45:04.989 16613-16613/com.bobomee.android.attrstest E/MyView: attrName = layout_height , attrVal = 200.0dip
01-25 23:45:04.989 16613-16613/com.bobomee.android.attrstest E/MyView: attrName = size , attrVal = 50.0dip

layout.xml

  <com.bobomee.android.attrstest.MyView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="@color/colorPrimary"
        android:textColor ="#00ffab"
        app:size="50dp" />

可见,background是一个ID:@2131427347,其他的都是实际值。

有了id,就可以通过id来得到具体值了。

修改代码如下:

dimens.xml

 <dimen name="width">200dp</dimen>
    <dimen name="height">200dp</dimen>
    <dimen name="size">50dp</dimen>

activity_main.xml

<com.bobomee.android.attrstest.MyView
        android:layout_width="@dimen/width"
        android:layout_height="@dimen/height"
        app:size="@dimen/size" />

MyView

int count = attrs.getAttributeCount();
        for (int i = 0; i < count; i++) {
            String attrName = attrs.getAttributeName(i);
            int resId = attrs.getAttributeResourceValue(i, 0);
            float attrVal = getResources().getDimension(resId);
            Log.e(T, "attrName = " + attrName + " , attrVal = " + attrVal);
        }

logcat信息:

01-26 00:21:14.189 28485-28485/com.bobomee.android.attrstest E/MyView: attrName = layout_width , attrVal = 400.0
01-26 00:21:14.189 28485-28485/com.bobomee.android.attrstest E/MyView: attrName = layout_height , attrVal = 400.0
01-26 00:21:14.189 28485-28485/com.bobomee.android.attrstest E/MyView: attrName = size , attrVal = 100.0

可以看到自定义属性值获取到了,可见TypedArray可以直接获取到引用类型的值,而不是id

declare-styleable

既然说attr不一定要定义在styleable中,那我么来尝试一下。
attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--<declare-styleable name="test">-->
        <attr name="size" format="dimension" />
        <!--<attr name="android:textColor" />-->
    <!--</declare-styleable>-->
</resources>

android:textColor如果不注釋掉,會報如下錯誤,也就是定义好的属性就可以直接使用了

Error:(3) Attribute "android:textColor" has already been defined

layout

 <com.bobomee.android.attrstest.MyView
        android:layout_width="@dimen/width"
        android:layout_height="@dimen/height"
        android:background="@color/colorPrimary"
        app:size="@dimen/size"
        android:textColor ="@color/textColor"
        android:textSize = "30sp"
        />

MyView

/**
 * Created by bobomee on 16/1/25.
 */
public class MyView extends View {

    String T = this.getClass().getSimpleName();

    private float mSize;
    private int mBackGround;
    private Paint mPaint;
    private float mTextSize;

    final int[] custom_attrs = {android.R.attr.textSize,android.R.attr.textColor,R.attr.size};
    final int TSIZE = 0;
    final int BACKGROUNG = 1;
    final int SIZE = 2;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        handleAttrs(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        handleAttrs(context, attrs, defStyleAttr);
    }

    private void handleAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
        //Retrieve styles attributes
//        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.test, defStyleAttr, 0);
//
          int defaultSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, context.getResources().getDisplayMetrics());
//        mSize = ta.getDimension(R.styleable.test_size, defaultSize);
//
//        mBackGround = ta.getColor(R.styleable.test_android_textColor, Color.WHITE);

        TypedArray ta = context.obtainStyledAttributes(attrs,custom_attrs);
        mSize = ta.getDimension(SIZE, defaultSize);
        mBackGround = ta.getColor(BACKGROUNG, Color.WHITE);
        mTextSize = ta.getDimension(TSIZE,defaultSize);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(mBackGround);

        Log.d(T, "mSize = " + mSize + " , mBackGround = " + mBackGround+ " , mTextSize = " + mTextSize);

//        int count = attrs.getAttributeCount();
//        for (int i = 0; i < count; i++) {
//            String attrName = attrs.getAttributeName(i);
//            int resId = attrs.getAttributeResourceValue(i, 0);
//            float attrVal = getResources().getDimension(resId);
//            Log.e(T, "attrName = " + attrName + " , attrVal = " + attrVal);
//        }

        ta.recycle();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(0, 0, mSize, mPaint);
    }
}

logcat

13541-13541/com.bobomee.android.attrstest D/MyView: mSize = 100.0 , mBackGround = -16711765 , mTextSize = 60.0

運行效果同上圖

在这里我们定义了一个attr数组,这里需要注意的是android本身定义的attr,需要放在数组的前面,如果有多个android属性,角标和数组定义需要对应即可,不用管layout中定义的顺序。
如果按照传统的方式,我们到android自动生成的R文件下看一下,会发现

  public final class R {
        public static final class attr {
            public static final int size=0x7f010110;
        }
        public static final class styleable {
            public static final int[] test = {
                    0x01010095, 0x01010098, 0x7f010110
            };
            public static final int test_android_textColor = 1;
            public static final int test_android_textSize = 0;
            public static final int test_size = 2;
        }
    }

通过declare-styleable可以在R文件下自动生成一个attr id 的数组 和下标,此外declare-styleable可以将相关属性分组,方便管理,同时以自定义View的名称命名,可以方便找到。

总结

  • 在android我们在attr下面申明属性,即可在R文件中生成相应的id,我们同时可以引用android定义好的attr属性。
  • 通过declare-styleable,可以在R文件中生成一个相应的attr集合的数组和角标,方便获取和分组管理,可以不用声明
  • TypedArray可以通过AttributeSet获取styleable数组中的自定义属性集合,从而获取属性值

参考:
Android 深入理解Android中的自定义属性

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 要设置自定义控件的布局,可以在自定义控件的构造函数中调用 `setLayout()` 方法,将布局设置为您想要的布局。例如,如果您想要使用垂直布局,则可以使用以下代码: ```java public class MyCustomView extends View { public MyCustomView(Context context) { super(context); // 创建一个垂直布局 LinearLayout layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); // 在布局中添加其他控件,如 TextView、Button 等 TextView textView = new TextView(context); textView.setText("Hello, world!"); layout.addView(textView); // 将布局设置为自定义控件的布局 setLayout(layout); } } ``` 请注意,`setLayout()` 方法是自定义控件类中的一个自定义方法,您需要根据自己的需求来实现它。在 `setLayout()` 方法中,您可以使用各种布局类(如 LinearLayout、RelativeLayout 等)来创建您想要的布局,并将其设置为自定义控件的布局。 ### 回答2: 自定义控件是在原有控件的基础上进行扩展或改造,使其具备特定的功能和样式。设置布局是指对自定义控件的布局进行定制,使其具备不同的排列方式和样式。 在自定义控件中设置布局可以通过以下步骤实现: 1. 在自定义控件的布局文件中定义布局容器,例如使用LinearLayout、RelativeLayout等。可以根据需要进行嵌套,构建复杂的布局结构。 2. 在自定义控件的构造方法中引入布局文件,通过LayoutInflater的inflate()方法将布局文件解析为View对象。 3. 通过findViewById()方法获取布局文件中定义的各个组件的引用,以便进行后续的操作。 4. 可以在自定义控件中添加自定义属性,通过在attrs.xml文件中定义属性,在布局文件中引用并在控件的构造方法中获取属性。例如可以定义属性设置文本大小、颜色等。 5. 在代码中对布局进行动态操作,例如设置组件的大小、位置、背景色等。可以通过LayoutParams来设置布局参数,例如设置宽高、边距等。 6. 在自定义控件的代码中实现各种事件的监听和响应,例如点击事件、长按事件等。可以通过setXXXListener()方法来设置事件监听器,并在监听器中编写对应的逻辑代码。 通过以上步骤,我们可以灵活地设置自定义控件的布局,并且根据需要进行各种样式的定制。这样可以大大提高了控件的灵活性和可重用性,并且使得界面布局更加符合需求。这对于开发中复杂的UI界面和特定的功能需求都是非常有帮助的。 ### 回答3: 自定义控件Android开发中常用的一种技术,通过自定义控件,我们可以实现更加丰富多样的布局效果。设置布局是自定义控件中的一个重要部分,可以通过设置不同的布局来达到不同的展示效果。 首先,要实现自定义控件的布局设置,我们需要在自定义控件的类中添加相应的方法和属性。常用的方法有onMeasure()和onLayout()。onMeasure()方法用于测量布局的宽高,通过setMeasuredDimension()方法设置测量结果;onLayout()方法用于定义布局中各个子控件的位置和大小。 在布局的设置过程中,我们可以使用常见的布局管理器,如LinearLayout、RelativeLayout等,对子控件进行排列和布局。在使用这些布局管理器时,可以通过LayoutParams属性来设置子控件的位置和大小,如设置权重、边距等。 在自定义控件中,我们还可以使用自定义的布局管理器,通过继承ViewGroup类来实现。在自定义布局管理器中,我们可以根据实际需求来定义子控件的排列规则,例如可以实现一个流式布局、网格布局等。 除了传统的布局管理器,我们还可以通过自定义xml布局文件,使用LayoutInflater来加载布局。在加载布局时,可以通过findViewById()方法来获取布局中的子控件,然后进行相应的设置和操作。 总之,自定义控件的布局设置是一个灵活多样的过程,通过灵活运用布局管理器和自定义xml布局文件,结合自定义的测量和排列方法,我们可以实现各种各样独特的布局效果,提升用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值