转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901
很多的Android入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章。先总结下自定义View的步骤:
1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
[ 3、重写onMesure ]
4、重写onDraw
我把3用[]标出了,所以说3不一定是必须的,当然了大部分情况下还是需要重写的。
1、自定义View的属性,首先在res/values/ 下建立一个attrs.xml , 在里面定义我们的属性和声明我们的整个样式。
<?xml version="1.0" encoding="utf-8"?>
<resources> <attr name="titleText" format="string" />
<attr name="titleTextColor" format="color" />
<attr name="titleTextSize" format="dimension" /> <declare-styleable name="CustomTitleView">
<attr name="titleText" />
<attr name="titleTextColor" />
<attr name="titleTextSize" />
</declare-styleable> </resources>
我们定义了字体,字体颜色,字体大小3个属性,format是值该属性的取值类型:
一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag;不清楚的可以google一把。
然后在布局中声明我们的自定义View
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"
android:layout_width="match_parent"
android:layout_height="match_parent" > <com.example.customview01.view.CustomTitleView
android:layout_width="200dp"
android:layout_height="100dp"
custom:titleText="3712"
custom:titleTextColor="#ff0000"
custom:titleTextSize="40sp" /> </RelativeLayout>
一定要引入 xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"我们的命名空间,后面的包路径指的是项目的package
2、在View的构造方法中,获得我们的自定义的样式
/**
* 文本
*/
private String mTitleText;
/**
* 文本的颜色
*/
private int mTitleTextColor;
/**
* 文本的大小
*/
private int mTitleTextSize; /**
* 绘制时控制文本绘制的范围
*/
private Rect mBound;
private Paint mPaint; public CustomTitleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public CustomTitleView(Context context) {
this(context, null);
} /**
* 获得我自定义的样式属性
*
* @param context
* @param attrs
* @param defStyle
*/
public CustomTitleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
/**
* 获得我们所定义的自定义样式属性
*/
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.CustomTitleView_titleText:
mTitleText = a.getString(attr);
break;
case R.styleable.CustomTitleView_titleTextColor:
// 默认颜色设置为黑色
mTitleTextColor = a.getColor(attr, Color.BLACK);
break;
case R.styleable.CustomTitleView_titleTextSize:
// 默认设置为16sp,TypeValue也可以把sp转化为px
mTitleTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
}
}
a.recycle();
/**
* 获得绘制文本的宽和高
*/
mPaint = new Paint();
mPaint.setTextSize(mTitleTextSize);
// mPaint.setColor(mTitleTextColor);
mBound = new Rect();
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
}
我们重写了3个构造方法,默认的布局文件调用的是两个参数的构造方法,所以记得让所有的构造调用我们的三个参数的构造,我们在三个参数的构造中获得自定义属性。
3、我们重写onDraw,onMesure调用系统提供的
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} @Override
protected void onDraw(Canvas canvas) {
mPaint.setColor(Color.YELLOW);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint); mPaint.setColor(mTitleTextColor);
canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
}
此时的效果是:
是不是觉得还不错,基本已经实现了自定义View。但是此时如果我们把布局文件的宽和高写成wrap_content,会发现效果并不是我们的预期:
系统帮我们测量的高度和宽度都是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。
所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMesure方法”:
重写之前先了解MeasureSpec的specMode,一共三种类型:
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
下面是我们重写onMeasure代码:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
float textWidth = mBounds.width();
int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = desired;
} if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
float textHeight = mBounds.height();
int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = desired;
} setMeasuredDimension(width, height);
}
现在我们修改下布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"
android:layout_width="match_parent"
android:layout_height="match_parent" > <com.example.customview01.view.CustomTitleView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:titleText="3712"
android:padding="10dp"
custom:titleTextColor="#ff0000"
android:layout_centerInParent="true"
custom:titleTextSize="40sp" /> </RelativeLayout>
现在的效果是:
完全复合我们的预期,现在我们可以对高度、宽度进行随便的设置了,基本可以满足我们的需求。
当然了,这样下来我们这个自定义View与TextView相比岂不是没什么优势,所有我们觉得给自定义View添加一个事件:
在构造中添加:
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mTitleText = randomText();
postInvalidate();
}
});
private String randomText() {
Random random = new Random();
Set<Integer> set = new HashSet<Integer>();
while (set.size() < 4) {
int randomInt = random.nextInt(10);
set.add(randomInt);
}
StringBuffer sb = new StringBuffer();
for (Integer i : set) {
sb.append("" + i);
} return sb.toString();
}
下面再来运行:
自定义View进阶
自定义View显示一张图片,下面包含图片的文本介绍,类似相片介绍什么的,不过不重要,主要是学习自定义View的用法么。
还记得上一篇讲的4个步骤么:
1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
[ 3、重写onMesure ]
4、重写onDraw
直接切入正题:
1、在res/values/attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources> <attr name="titleText" format="string" />
<attr name="titleTextSize" format="dimension" />
<attr name="titleTextColor" format="color" />
<attr name="image" format="reference" />
<attr name="imageScaleType">
<enum name="fillXY" value="0" />
<enum name="center" value="1" />
</attr> <declare-styleable name="CustomImageView">
<attr name="titleText" />
<attr name="titleTextSize" />
<attr name="titleTextColor" />
<attr name="image" />
<attr name="imageScaleType" />
</declare-styleable> </resources>
2、在构造中获得我们的自定义属性:
public CustomImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, defStyle, 0); int n = a.getIndexCount(); for (int i = 0; i < n; i++) {
int attr = a.getIndex(i); switch (attr) {
case R.styleable.CustomImageView_image:
mImage = BitmapFactory.decodeResource(getResources(), a.getResourceId(attr, 0));
break;
case R.styleable.CustomImageView_imageScaleType:
mImageScale = a.getInt(attr, 0);
break;
case R.styleable.CustomImageView_titleText:
mTitle = a.getString(attr);
break;
case R.styleable.CustomImageView_titleTextColor:
mTextColor = a.getColor(attr, Color.BLACK);
break;
case R.styleable.CustomImageView_titleTextSize:
mTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break; }
}
a.recycle();
rect = new Rect();
mPaint = new Paint();
mTextBound = new Rect();
mPaint.setTextSize(mTextSize);
// 计算了描绘字体需要的范围
mPaint.getTextBounds(mTitle, 0, mTitle.length(), mTextBound);
}
3、重写onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 设置宽度
*/
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec); if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
{
Log.e("xxx", "EXACTLY");
mWidth = specSize;
} else {
// 由图片决定的宽
int desireByImg = getPaddingLeft() + getPaddingRight() + mImage.getWidth();
// 由字体决定的宽
int desireByTitle = getPaddingLeft() + getPaddingRight() + mTextBound.width(); if (specMode == MeasureSpec.AT_MOST)// wrap_content
{
int desire = Math.max(desireByImg, desireByTitle);
mWidth = Math.min(desire, specSize);
Log.e("xxx", "AT_MOST");
}
} /***
* 设置高度
*/ specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
if (specMode == MeasureSpec.EXACTLY)// match_parent , accurate
{
mHeight = specSize;
} else {
int desire = getPaddingTop() + getPaddingBottom() + mImage.getHeight() + mTextBound.height();
if (specMode == MeasureSpec.AT_MOST)// wrap_content
{
mHeight = Math.min(desire, specSize);
}
}
setMeasuredDimension(mWidth, mHeight);
}
4、重写onDraw
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
/**
* 边框
*/
mPaint.setStrokeWidth(4);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.CYAN);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint); rect.left = getPaddingLeft();
rect.right = mWidth - getPaddingRight();
rect.top = getPaddingTop();
rect.bottom = mHeight - getPaddingBottom(); mPaint.setColor(mTextColor);
mPaint.setStyle(Style.FILL);
/**
* 当前设置的宽度小于字体需要的宽度,将字体改为xxx...
*/
if (mTextBound.width() > mWidth) {
TextPaint paint = new TextPaint(mPaint);
String msg = TextUtils.ellipsize(mTitle, paint, (float) mWidth - getPaddingLeft() - getPaddingRight(), TextUtils.TruncateAt.END).toString();
canvas.drawText(msg, getPaddingLeft(), mHeight - getPaddingBottom(), mPaint); } else {
// 正常情况,将字体居中
canvas.drawText(mTitle, mWidth / 2 - mTextBound.width() * 1.0f / 2, mHeight - getPaddingBottom(), mPaint);
} // 取消使用掉的快
rect.bottom -= mTextBound.height(); if (mImageScale == IMAGE_SCALE_FITXY) {
canvas.drawBitmap(mImage, null, rect, mPaint);
} else {
// 计算居中的矩形范围
rect.left = mWidth / 2 - mImage.getWidth() / 2;
rect.right = mWidth / 2 + mImage.getWidth() / 2;
rect.top = (mHeight - mTextBound.height()) / 2 - mImage.getHeight() / 2;
rect.bottom = (mHeight - mTextBound.height()) / 2 + mImage.getHeight() / 2; canvas.drawBitmap(mImage, null, rect, mPaint);
} }
代码,结合注释和第一篇View的使用,应该可以看懂,不明白的留言。下面我们引入我们的自定义View:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:zhy="http://schemas.android.com/apk/res/com.zhy.customview02"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <com.zhy.customview02.view.CustomImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:padding="10dp"
zhy:image="@drawable/ic_launcher"
zhy:imageScaleType="center"
zhy:titleText="hello andorid ! "
zhy:titleTextColor="#ff0000"
zhy:titleTextSize="30sp" /> <com.zhy.customview02.view.CustomImageView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:padding="10dp"
zhy:image="@drawable/ic_launcher"
zhy:imageScaleType="center"
zhy:titleText="helloworldwelcome"
zhy:titleTextColor="#00ff00"
zhy:titleTextSize="20sp" /> <com.zhy.customview02.view.CustomImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:padding="10dp"
zhy:image="@drawable/lmj"
zhy:imageScaleType="center"
zhy:titleText="妹子~"
zhy:titleTextColor="#ff0000"
zhy:titleTextSize="12sp" /> </LinearLayout>
我特意让显示出现3中情况:
1、字体的宽度大于图片,且View宽度设置为wrap_content
2、View宽度设置为精确值,字体的长度大于此宽度
3、图片的宽度大于字体,且View宽度设置为wrap_content
看看显示效果:
View自定义属性
1、引言
对于自定义属性,大家肯定都不陌生,遵循以下几步,就可以实现:
- 自定义一个CustomView(extends View )类
- 编写values/attrs.xml,在其中编写styleable和item等标签元素
- 在布局文件中CustomView使用自定义的属性(注意namespace)
- 在CustomView的构造方法中通过TypedArray获取
ps:如果你对上述几个步骤不熟悉,建议先熟悉下,再继续~
那么,我有几个问题:
- 以上步骤是如何奏效的?
- styleable 的含义是什么?可以不写嘛?我自定义属性,我声明属性就好了,为什么一定要写个styleable呢?
- 如果系统中已经有了语义比较明确的属性,我可以直接使用嘛?
- 构造方法中的有个参数叫做
AttributeSet
(eg: MyTextView(Context context, AttributeSet attrs) )这个参数看名字就知道包含的是参数的数组,那么我能不能通过它去获取我的自定义属性呢? - TypedArray是什么鬼?从哪冒出来的,就要我去使用?
恩,针对这几个问题,大家可以考虑下,如何回答呢?还是说:老子会背上述4个步骤就够了~~
2、常见的例子
接下来通过例子来回答上述问题,问题的回答顺序不定~~大家先看一个常见的例子,即上述几个步骤的代码化。
- 自定义属性的声明文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="test">
<attr name="text" format="string" />
<attr name="testAttr" format="integer" />
</declare-styleable>
</resources>
- 自定义View类
package com.example.test; import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View; public class MyTextView extends View { private static final StringTAG=MyTextView.class.getSimpleName(); public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.test);
String text = ta.getString(R.styleable.test_testAttr);
int textAttr = ta.getInteger(R.styleable.test_text, -1);
Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);
ta.recycle();
} }
- 布局文件中使用
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:zhy="http://schemas.android.com/apk/res/com.example.test"
android:layout_width="match_parent"
android:layout_height="match_parent" > <com.example.test.MyTextView
android:layout_width="100dp"
android:layout_height="200dp"
zhy:testAttr="520"
zhy:text="helloworld" /> </RelativeLayout>
ok,大家花3s扫一下,运行结果为:
MyTextView: text = helloworld , textAttr = 520
应该都不意外吧,注意下,我的styleable的name写的是test,所以说这里并不要求一定是自定义View的名字。
3、AttributeSet与TypedArray
下面考虑:
构造方法中的有个参数叫做
AttributeSet
(eg: MyTextView(Context context, AttributeSet attrs) )这个参数看名字就知道包含的是参数的集合,那么我能不能通过它去获取我的自定义属性呢?
首先AttributeSet
中的确保存的是该View声明的所有的属性,并且外面的确可以通过它去获取(自定义的)属性,怎么做呢?
其实看下AttributeSet
的方法就明白了,下面看代码。
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
int count = attrs.getAttributeCount();
for (int i = 0; i < count; i++) {
String attrName = attrs.getAttributeName(i);
String attrVal = attrs.getAttributeValue(i);
Log.e(TAG, "attrName = " + attrName + " , attrVal = " +attrVal);
}
// ==>use typedarray ...
}
输出:
MyTextView(4136): attrName = layout_width , attrVal = 100.0dip
MyTextView(4136): attrName = layout_height , attrVal = 200.0dip
MyTextView(4136): attrName = text , attrVal = helloworld
MyTextView(4136): attrName = testAttr , attrVal = 520
结合上面的布局文件,你发现了什么?
我擦,果然很神奇,真的获得所有的属性,恩,没错,通过AttributeSet
可以获得布局文件中定义的所有属性的key和value(还有一些方法,自己去尝试),那么是不是说TypedArray这个鬼可以抛弃了呢?答案是:NO!
。
现在关注下一个问题:
TypedArray是什么鬼?从哪冒出来的,就要我去使用?
我们简单修改下,布局文件中的MyTextView的属性。
<com.example.test.MyTextView
android:layout_width="@dimen/dp100"
android:layout_height="@dimen/dp200"
zhy:testAttr="520"
zhy:text="@string/hello_world" />
现在再次运行的结果是:
MyTextView(4692): attrName = layout_width , attrVal = @2131165234
MyTextView(4692): attrName = layout_height , attrVal = @2131165235
MyTextView(4692): attrName = text , attrVal = @2131361809
MyTextView(4692): attrName = testAttr , attrVal = 520
>>use typedarray
MyTextView(4692): text = Hello world! , textAttr = 520
发现了什么?通过AttributeSet
获取的值,如果是引用都变成了@+数字的字符串。你说,这玩意你能看懂么?那么你看看最后一行使用TypedArray获取的值,是不是瞬间明白了什么。
TypedArray
其实是用来简化我们的工作的,比如上例,如果布局中的属性的值是引用类型(比如:@dimen/dp100),如果使用AttributeSet
去获得最终的像素值,那么需要第一步拿到id,第二步再去解析id。而TypedArray正是帮我们简化了这个过程。
贴一下:如果通过AttributeSet
获取最终的像素值的过程:
int widthDimensionId = attrs.getAttributeResourceValue(0, -1);
Log.e(TAG, "layout_width= "+getResources().getDimension(widthDimensionId));
ok,现在别人问你TypedArray存在的意义,你就可以告诉他了。
4、declare-styleable
我们已经解决了两个问题,接下来,我们看看布局文件,我们有一个属性叫做:zhy:text
。
总所周知,系统提供了一个属性叫做:android:text
,那么我觉得直接使用android:text
更nice,这样的话,考虑问题:
如果系统中已经有了语义比较明确的属性,我可以直接使用嘛?
答案是可以的,怎么做呢?
直接在attrs.xml中使用android:text
属性。
<declare-styleable name="test">
<attr name="android:text" />
<attr name="testAttr" format="integer" />
</declare-styleable>
注意,这里我们是使用已经定义好的属性,不需要去添加format
属性(注意声明和使用的区别,差别就是有没有format)。
然后在类中这么获取:ta.getString(R.styleable.test_android_text)
;布局文件中直接android:text="@string/hello_world"
即可。
这里提一下,系统中定义的属性,其实和我们自定义属性的方式类似,你可以在sdk/platforms/android-xx/data/res/values
该目录下看到系统中定义的属性。然后你可以在系统提供的View(eg:TextView)的构造方法中发现TypedArray获取属性的代码(自己去看一下)。
ok,接下来,我在想,既然declare-styleable
这个标签的name都能随便写,这么随意的话,那么考虑问题:
styleable 的含义是什么?可以不写嘛?我自定义属性,我声明属性就好了,为什么一定要写个styleable呢?
其实的确是可以不写的,怎么做呢?
- 首先删除declare-styleable的标签
那么现在的attrs.xml为:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="testAttr" format="integer" />
</resources>
* MyTextView实现
package com.example.test; import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View; public class MyTextView extends View { private static final String TAG = MyTextView.class.getSimpleName(); private static final int[] mAttr = { android.R.attr.text, R.attr.testAttr };
private static final int ATTR_ANDROID_TEXT = 0;
private static final int ATTR_TESTATTR = 1; public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs); // ==>use typedarray
TypedArray ta = context.obtainStyledAttributes(attrs, mAttr); String text = ta.getString(ATTR_ANDROID_TEXT);
int textAttr = ta.getInteger(ATTR_TESTATTR, -1);
//输出 text = Hello world! , textAttr = 520
Log.e(TAG, "text = " + text + " , textAttr = " + textAttr); ta.recycle();
} }
貌似多了些代码,可以看到我们声明了一个int数组,数组中的元素就是我们想要获取的attr的id。并且我们根据元素的在数组中的位置,定义了一些整形的常量代表其下标,然后通过TypedArray
进行获取。
可以看到,我们原本的:
R.styleable.test => mAttr
R.styleable.test_text => ATTR_ANDROID_TEXT(0)
R.styleable.test_testAttr => ATTR_TESTATTR(1)
那么其实呢?android在其内部也会这么做,按照传统的写法,它会在R.java生成如下代码:
public static final class attr {
public static final int testAttr=0x7f0100a9;
}
public static final class styleable {
public static final int test_android_text = 0;
public static final int test_testAttr = 1;
public static final int[] test = {
0x0101014f, 0x7f0100a9
};
}
ok,根据上述你应该发现了什么。styleale的出现系统可以为我们完成很多常量(int[]数组,下标常量)等的编写,简化我们的开发工作(想想如果一堆属性,自己编写常量,你得写成什么样的代码)。那么大家肯定还知道declare-styleable
的name属性,一般情况下写的都是我们自定义View的类名。主要为了直观的表达,该declare-styleable
的属性,都是改View所用的。
其实了解该原理是有用的,详见:Android 自定义控件 优雅实现元素间的分割线
ok,现在5个问题,回答了4个,第一个问题:
自定义属性的几个步骤是如何奏效的?
恩,上述以及基本涵盖了这个问题的答案,大家自己总结,所以:略。
总结下今天的博客。
- attrs.xml里面的declare-styleable以及item,android会根据其在R.java中生成一些常量方便我们使用(aapt干的),本质上,我们可以不声明declare-styleable仅仅声明所需的属性即可。
- 我们在View的构造方法中,可以通过
AttributeSet
去获得自定义属性的值,但是比较麻烦,而TypedArray
可以很方便的便于我们去获取。 - 我们在自定义View的时候,可以使用系统已经定义的属性。
Android自定义View的更多相关文章
-
Android自定义View 画弧形,文字,并增加动画效果
一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类 B ...
-
(转)[原] Android 自定义View 密码框 例子
遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...
-
Android 自定义View合集
自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...
-
Android 自定义View (五)&mdash;&mdash;实践
前言: 前面已经介绍了<Android 自定义 view(四)-- onMeasure 方法理解>,那么这次我们就来小实践下吧 任务: 公司现有两个任务需要我完成 (1)监测液化天然气液压 ...
-
Android 自定义 view(四)&mdash;&mdash; onMeasure 方法理解
前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...
-
Android 自定义 view(三)&mdash;&mdash; onDraw 方法理解
前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...
-
Android 自定义view(二) &mdash;&mdash; attr 使用
前言: attr 在前一篇文章<Android 自定义view -- attr理解>已经简单的进行了介绍和创建,那么这篇文章就来一步步说说attr的简单使用吧 自定义view简单实现步骤 ...
-
Android 自定义View
Android 自定义View流程中的几个方法解析: onFinishInflate():从布局文件.xml加载完组件后回调 onMeasure() :调用该方法负责测量组件大小 onSizeChan ...
-
Android自定义View之CircleView
Android自定义View之CircleView 版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请表明出处:http://www.cnblogs.com/cavalier-/p/5999 ...
-
Android 自定义View及其在布局文件中的使用示例(三):结合Android 4.4.2_r1源码分析onMeasure过程
转载请注明出处 http://www.cnblogs.com/crashmaker/p/3549365.html From crash_coder linguowu linguowu0622@gami ...
随机推荐
-
Java -- 子类使用super调用父类的方法A,A 调用了方法B,子类也override方法B,那么super.A()最终调用到了子类的B方法
public class SuperClass{ public void printA(){ System.out.print("SuperClass-printA"); prin ...
-
JavaScript学习笔记(备忘录)
===运算符 判断数值和类型是否相等.如: console.log('s'==='s') //输出trueconsole.log('1'===1) //输出false
-
js之with
网上找资料的时候看到有js代码里出现了大量的with语句,有点好奇,这里做下总结: 1)简要说明 with 语句可以方便地用来引用某个特定对象中已有的属性,但是不能用来给对象添加属性. ...
-
Oracle问题之literal does not match format string
问题: oerr ora 186101861, 00000, "literal does not match format string"// *Cause: Literals i ...
-
Mysql 测试题
一. 表结构和数据 作业要求 /* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL ...
-
eclipse换了高版本的maven插件后报错:org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project
在给eclipse换了高版本的maven插件后,引入jar包报如下的错误: org.apache.maven.archiver.MavenArchiver.getManifest(org.apach ...
-
Hadoop-HA机制工作原理
一.Hadoop 系统架构 1.1 Hadoop1.x和Hadoop2.x 架构 在介绍HA之前,我们先来看下Hadoop的系统架构,这对于理解HA是至关重要的.Hadoop 1.x之前,其官方架构如 ...
-
函数语法:Js之on和addEventListener的使用与不同
一.addEventListener语法 DOM标准:elem.addEventListener("事件名",函数对象,是否在捕获阶段触发) ---是否在捕获阶段触发= true/ ...
-
Bash 笔记
获取当前工作目录 basepath=$(cd `dirname $0`; pwd) 源文 : https://sexywp.com/bash-how-to-get-the-basepath-of-cu ...
-
Jmeter-使用Ultimate Thread Group插件来设置负载场景
前言: Jmeter插件相关请移步:https://www.jianshu.com/p/130c7fddeddf 自定义线程组:jp@gc - Ultimate Thread Group,功能强大,可 ...