自定义view大杂烩

   日期:2020-07-18     浏览:89    评论:0    
核心提示:文章目录自定义控件自定义控件:第一步自定义控件:第二步自定义控件:总结绘制view视图绘制大小:MeasureSpec测量模式开始实践设置view大小如何自定义view1.demo2.添加wrap_content的支持3.添加padding的支持[margin是由父布局控制的]4.为了方便配置,使用自定义属性总结:view的测量1 view.post(runnalbe)2.进行测量案例:api大法好呀前天看了同事的自定义view博客.也对view的绘制也做一个总结. 高手请指教,小白可以借鉴.自定义控件

文章目录

        • 自定义控件
          • 自定义控件:第一步
          • 自定义控件:第二步
          • 自定义控件:总结
        • 绘制view视图
          • 绘制大小:MeasureSpec
            • 测量模式
          • 开始实践
          • 设置view大小
        • 如何自定义view
          • 1.demo
          • 2.添加wrap_content的支持
          • 3.添加padding的支持[margin是由父布局控制的]
          • 4.为了方便配置,使用自定义属性
          • 总结:
        • view的测量
          • 1 view.post(runnalbe)
          • 2.进行测量
          • 案例:
        • api大法好呀

前天看了同事的自定义view博客.也对view的绘制也做一个总结. 高手请指教,小白可以借鉴.

自定义控件

自定义控件:第一步

为了将相同的布局文件在不同的地方使用,不用ctrl+c,ctrl+v,修改时再到处ctrl+f.所以将这个布局文件抽取出来,然后在需要使用的地方引用.

创建文件: layout_two_button.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
    <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1" android:text="被观察" />

    <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1" android:text="观察" />
</LinearLayout>

在需要的地方引用就可以了

    <include layout="@layout/layout_two_button" />
自定义控件:第二步

可是这些布局的点击函数大多相同,为了不在java文件中再设置相同的点击事件,

而xml只是布局文件,不是对象是没有点击事件的.继续将这些抽取出来的文件,变成类对象.

public class ButtonLineLayout extends LinearLayout {
    public ButtonLineLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        View inflate = LayoutInflater.from(context).inflate(R.layout.layout_two_button, this);
        inflate.findViewById(R.id.button1).setOnClickListener(v -> Toast.makeText(context, "button1", Toast.LENGTH_SHORT).show());
    }
}

将创建的类对象在需要的文件中引用

    <com.ui.material.ButtonLineLayout android:layout_width="match_parent" android:layout_height="wrap_content" />
自定义控件:总结

说到底,这只不过是一个特殊的lineLayout,里面添加了我们视图,并赋予了点击事件而已。

绘制view视图

小菜开胃,开始正餐。

绘制大小:MeasureSpec

首先来认识一个变量MeaureSpec. MeasureSpec 是一个32位的Int值。

MeasureSpec的前2位是表示测量模式,后面30位是视图的大小。

测量模式
  • exactly : 精确大小。
  • at_most: 限定了最大值。
  • unspecified : 不限制大小。你想多大就多大。一般用不到,忘记它吧。它是你得不到的人。

子view MeasureSpec 是由 父布局的 MeasureSpec 和 自身 布局属性 决定的.

测量模式和wrapCont的关系简单的记:

  • 指定大小 = exactly
  • wrapContent = at_most
  • matchParent 视情况而定

Exactly下的matchParent就是Exactly的.
at_most下的matchParent就是at_most .上天注定的

首先纠正一个误区。textVeiw中的wrap_content可以实现自适应大小,是人家做了代码逻辑处理。

不是把你的view设置了wrap_content也不用管他的大小了。。

它不会孙猴子的铁棒可以你想多大就可以多大。

在源码中view的measure->onMeasure->getDeafultSize方法中可以看到at_most,exactly 没有区别。

哎,不给你看,来打我呀。

开始实践
设置view大小
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSpec = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    
    if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
        setMeasuredDimension(23, 23);
    }else if(widthMode == MeasureSpec.AT_MOST ){
        setMeasuredDimension(23,heightSize);
    }else if(heightMode == MeasureSpec.AT_MOST){
        setMeasuredDimension(widthSpec,23);
    }

}

总结 :

MeasureSpec.getMode 获取模式

MeasureSpec.getSize 获取大小

setMeasuredDimension 设置view大小

如何自定义view

  • 继承TextView 对view实现功能的扩展
  • 继承Linelayout对viewGroup实现扩展
  • 继承View
  • 继承viewGroup

继承系统控件只是对功能进行扩展,不做过多描述.
下面看如何继承view实现绘制.

1.demo
public class CirecleView extends View {
    private int mColor = Color.RED;
    private Paint mPaint = new Paint(ANTI_ALIAS_FLAG);

    
    public CirecleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(mColor);
        
        int width = getWidth();
        int height = getHeight();
        int radius = Math.min(width, height) / 2;
        canvas.drawCircle(width / 2, height / 2, radius, mPaint);
        
    }
}
2.添加wrap_content的支持
public class CirecleView extends View {
    
    ******
    ~~~~~~

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        //由一个是精确模式,就取精确模式的值.
        if (widthMode == MeasureSpec.EXACTLY || heightMode == MeasureSpec.EXACTLY) {
            int size = Math.min(widthSize, heightSize);
            setMeasuredDimension(size, size);
        } else {
            //否则使用默认值
            int defaultSize = 400;
            setMeasuredDimension(defaultSize, defaultSize);
        }

    }
}
3.添加padding的支持[margin是由父布局控制的]
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setColor(mColor);
    //首先明确一点,视图的宽高已经固定,padding越大,半径越小
    int width = getWidth() - (getPaddingLeft() + getPaddingRight()) ;
    int height = getHeight() - (getPaddingTop() + getPaddingBottom()) ;
    int radius = Math.min(width, height) / 2;
    //然后左,上决定位置
    canvas.drawCircle(width / 2 + getPaddingLeft(), height / 2 + getPaddingTop(), radius, mPaint);
}
4.为了方便配置,使用自定义属性

attrs.xml中设置 类,属性,类型

<?xml version="1.0" encoding="utf-8"?>
<resources>
    //类
    <declare-styleable name="CirecleView">
        //属性                        //类型
        <attr name="circle_color" format="color"></attr>
    </declare-styleable>

</resources>
public CirecleView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    //获取类下所有的属性
    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CirecleView);
    mColor = typedArray.getColor(R.styleable.CirecleView_circle_color, 0xff0000);
    typedArray.recycle();
}
总结:

onMeasure 中添加wrapContent的判断,显示大小

onDraw中添加padding的支持

再提供一些自定义的属性.一个简单的自定义view就完成了

view的测量

我们可以通过setMeasuredDimension来设置view的大小,但是其他view的大小我们如何得知呢.

getWidth()?getMeasureWidth()?

view的绘制和activity中生命周期没有关系。
onResume时,acitivity都可以可见互动了,你还没绘制好,玩我呢。

通常做法:

1 view.post(runnalbe)
view.post(() -> {
            view.getMeasuredHeight();          
        });

补充说明:

getMeasuredHeight 是测量大小,
getHight是布局大小。
一般而言两者没有区别,但是可以有,只不过没啥意义. 【ps:我的面试题,当时一脸懵逼】

重写Layout方法后,测量宽度getMeasuredHeight 会比getHight小100.

    @Override
    public void layout(int l, int t, int r, int b) {
        super.layout(l, t, r+100, b+100);
    }
2.进行测量

如果是matchParent模式:

在绘制view的时候还不知道父布局的大小,所以无解

如果是wrapContent模式

 //创建一个MeaureSpec:告知view的最大值和测量模式
 int heightSpec = weidthSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST)
 //给爷去测量
 view.measure(weidthSpec,heightSpec)
 //获取测量的数值
 view.getMeasuredWidth();

ps:需要准确的告知测量模式

案例:
        Log.d("test",cire.measuredHeight.toString()) // 0
        val widthSpec = MeasureSpec.makeMeasureSpec(resources.getDimension(R.dimen.fab_margin_50).toInt(), MeasureSpec.EXACTLY)
        val  heightSpec= MeasureSpec.makeMeasureSpec(Int.MAX_VALUE shr 2, MeasureSpec.AT_MOST)
        cire.measure(widthSpec,heightSpec)
        Log.d("test",cire.measuredHeight.toString()) // 正确结果131
        Log.d("test",cire.height.toString())  // 0 

结果发现getHeight测量后还是0,getMeasureHeight就已经有值了
上面刚说getMeasuredHeight 和 getHight没有区别 ,结果就啪啪的打脸.我不要面子的嘛.-.-

就画一个圈,就说会绘制view了.你在敷衍谁呢,好歹告诉我怎么画一个框呀.好满足你.api大法上!

api大法好呀

借鉴T9的第三个三角的博客,在这里表示感谢.

		
        mPaint.setColor(Color.GRAY);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setTypeface(Typeface.SANS_SERIF);//字体
        mPaint.setFakeBoldText(true);//粗体
        mPaint.setStrikeThruText(true);//删除线
        mPaint.setUnderlineText(true); //下划线
        mPaint.setTextSkewX(-0.4f);//倾斜
// mPaint.setLetterSpacing(0.8f);//字符间距
// mPaint.setTextAlign(Paint.Align.RIGHT);//对齐
        mPaint.setStrokeWidth(5);
        mPaint.setTextSize(200);
        canvas.drawText("practice",20,200,mPaint);

        
        Rect bounds = new Rect();
        mPaint.getTextBounds("practice",0,"practice".length(),bounds);
        bounds.left += 20;
        bounds.right += 20;
        bounds.top += 200;
        bounds.bottom += 200;
        canvas.drawRect(bounds,mPaint);

        
        float textWidth = mPaint.measureText("practice");
// bounds.width();
        canvas.drawLine(20,200, textWidth += 20,200,mPaint);


        
        Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
        float height1 = fontMetrics.descent - fontMetrics.ascent;
        float height2 = fontMetrics.bottom - fontMetrics.top;

        
        canvas.clipRect(100,100,400,400);
        Bitmap bitmap =   BitmapFactory.decodeResource(getResources(), R.drawable.ic_action_name);
        canvas.drawBitmap(bitmap,100,100,mPaint);

        
        Path path = new Path();
        path.addCircle(500,500,400, Path.Direction.CCW);
        canvas.clipPath(path);

        
        canvas.translate(300,100);

        
        canvas.rotate(200);

        
        canvas.scale(1.5f,0.8f,100,100);

        
        canvas.drawCircle(20, 20, 4, mPaint);
        
        canvas.drawRect(20,0,20,90,mPaint);
 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服