自定义的View练习
程序员文章站
2022-05-30 20:08:11
...
部分资料 来自安卓群英传 https://blog.csdn.net/a120705230/article/details/51970864
部分资料来自于在那桌开发艺术探索
一\实现一个简单的自定义View---画一个圆
public class Circle extends View {
//画笔颜色
private int mColor;
//新建画笔,Paint.ANTI_ALIAS_FLAG,表示抗锯齿
private Paint mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
//View的最小宽度和高度
private int mWidth;
private int mHeight;
//构造器,分两类,一个是默认构造器,一类是带属性的构造器,第二类构造器通过重载,都指向Circle(Context context, @Nullable AttributeSet attrs, int defStyleAttr)
public Circle(Context context) {
super(context);
init();
}
public Circle(Context context, @Nullable AttributeSet attrs) {
this(context,attrs,0);
}
public Circle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//获取View的颜色
TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.Circle);
mColor=a.getColor(R.styleable.Circle_circle_color, Color.RED);
a.recycle();
//初始化画笔颜色跟最小宽度高度
init();
}
private void init() {
mPaint.setColor(mColor);
mWidth=100;
mHeight=100;
}
//MeasureSpec.AT_MOST wrap_content
//MeasureSpec.EXACTLY match_parent
//自定义View,需要对wrap和match做区分,默认的实现,wrap等同于match
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMod=MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize=MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMod=MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize=MeasureSpec.getSize(heightMeasureSpec);
//如果是宽度高度都为wrap,那么设置测量的宽度高度为mWidth和mHeight
if (widthSpecMod==MeasureSpec.AT_MOST&&heightSpecMod==MeasureSpec.AT_MOST){
setMeasuredDimension(mWidth,mHeight);
}else if (widthSpecMod==MeasureSpec.AT_MOST){
setMeasuredDimension(mWidth,heightSpecSize);
}else if (heightSpecMod==MeasureSpec.AT_MOST){
setMeasuredDimension(widthSpecSize,mHeight);
}
}
//半径是宽度和高度最小值的一半,圆心,x在左padding加半径,y在顶部padding加半径.
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int paddingl=getPaddingLeft();
int paddingr=getPaddingRight();
int paddingt=getPaddingTop();
int paddingb=getPaddingBottom();
int width=getWidth()-paddingl-paddingr;
int height=getHeight()-paddingt-paddingb;
int Radius=Math.min(width,height)/2;
canvas.drawCircle(paddingl+width/2,paddingt+height/2,Radius,mPaint);
}
}
attrs自定义属性文件
位置在value文件夹下,一般取名风格,attrs_circle.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Circle">
<attr name="circle_color" format="color"/>
</declare-styleable>
</resources>
在构造器中通过
TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.Circle);
mColor=a.getColor(R.styleable.Circle_circle_color, Color.RED);
a.recycle();
来获取自定义属性
实现效果
二\实现一个自定义ViewGroup
简单的实现效果
让两个textView并列排放
public class TextViewNest extends ViewGroup{
//最小宽度高度
private int mWidth;
private int mHeight;
//子View之间的间距
private float mMargin=8* getContext().getResources().getDisplayMetrics().density;
//构造方法,同自定义View
public TextViewNest(Context context) {
super(context);
init();
}
public TextViewNest(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public TextViewNest(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.TextViewNest);
mMargin=a.getDimension(R.styleable.TextViewNest_m_margin,0);
a.recycle();
init();
}
//初始化最小宽度高度
private void init() {
mWidth=100;
mHeight=100;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取ViewGroup的测量模式和测量长度
int widthSpecMod=MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize=MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMod=MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize=MeasureSpec.getSize(heightMeasureSpec);
//累计最大宽度,初始值为ViewGoup的左右padding值
int maxWidth=getPaddingLeft()+getPaddingRight();
//累计最大高度,初始值为ViewGroup的上下padding值
int maxHeight=getPaddingTop()+getPaddingBottom();
//首先测量每一个childView
for (int i=0;i<getChildCount();i++) {
View childView = getChildAt(i);
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
}
//当内部没有childView的时候,设置ViewGroup宽高为最小值
if (getChildCount()==0){
setMeasuredDimension(mWidth,mHeight);
//当宽高为wrap模式时,获取每一个childView的测量值,高度,高度为上下padding值加子View高度的最大值.宽度为左右padding加上所有子View的宽度,宽度不小于最小宽度,高度不小于最小高度
}else if (widthSpecMod==MeasureSpec.AT_MOST&&heightSpecMod==MeasureSpec.AT_MOST){
for (int i=0;i<getChildCount();i++){
View childView=getChildAt(i);
maxHeight=Math.max(maxHeight,getPaddingTop()+getPaddingBottom()+childView.getMeasuredHeight());
maxWidth+=childView.getMeasuredWidth();
}
//把子View之间的间距值mMargin加上
maxWidth+=mMargin*(getChildCount()-1);
setMeasuredDimension(Math.max(mWidth,maxWidth),Math.max(mHeight,maxHeight));
//当宽度为wrap模式时,设置高度为ViewGroup的测量值,宽度,为左右padding加上所有内部View的宽度值,宽度不小于最小宽度
}else if (widthSpecMod==MeasureSpec.AT_MOST){
for (int i=0;i<getChildCount();i++){
View childView=getChildAt(i);
maxWidth+=childView.getMeasuredWidth();
}
maxWidth+=mMargin*(getChildCount()-1);
setMeasuredDimension(Math.max(mWidth,maxWidth),heightSpecSize);
//当高度为wrap模式时,宽度设置为ViewGroup的测量值,高度设置为上下padding内部View高度的最大值,高度不小于最小高度
}else if (heightSpecMod==MeasureSpec.AT_MOST) {
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
maxHeight = Math.max(maxHeight, getPaddingTop() + getPaddingBottom() + childView.getMeasuredHeight());
}
setMeasuredDimension(widthSpecSize, Math.max(mHeight, maxHeight));
}
}
@Override
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
int paddingl=getPaddingLeft();
int paddingr=getPaddingRight();
int paddingt=getPaddingTop();
int paddingb=getPaddingBottom();
int mChildCount=getChildCount();
//起始点的x坐标
int mLeft=paddingl;
//起始点的y坐标
int mTop=paddingt;
//当子View可见的时候,计算子View的宽高,起始点一支,右下角终点由起点坐标加上自身宽高得到.同时,起点x要平移width,得到下个View的起点
for (int n =0;n<mChildCount;n++){
View childView=getChildAt(n);
if (childView.getVisibility()!=GONE){
int width=childView.getMeasuredWidth();
int height=childView.getMeasuredHeight();
childView.layout(mLeft,mTop,width+mLeft,height+mTop);
mLeft+=width;
if (n!=mChildCount-1){
mLeft+=mMargin;
}
}
}
}
}
自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TextViewNest">
<attr name="m_margin" format="dimension"/>
</declare-styleable>
</resources>
实现效果
<com.example.mycanlendartest.TextViewNest
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:padding="30dp"
app:m_margin="10dp"
android:background="@android:color/holo_blue_light"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是1"
android:textSize="14sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是2"
android:textSize="14sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是3"
android:textSize="14sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是4"
android:textSize="14sp"/>
</com.example.mycanlendartest.TextViewNest>