自定义控件

自定义控件流程

  • 加载:初始化数据(画笔paint颜色,样式;)
  • 测量
  • 布局
  • 绘制

3个 构造方法

  • 第1个一般用来new 控件
  • 第2个用在布局文件中,系统解析xml文件吧属性封装成集合,调用第2个构造(生命在xml中要包名+类名)
  • 第3个可以加入样式
    注意:前两个构造用this,不管用那个构造最后都走到第三个构造,这样就可以集中管理;否则,写在第一个里的方法(初始化用,避免要写三个同样的方法),直接写在xml中有系统创建是不会调用。而集中管理则会统一调用第三个。
public class CanvasTestView extends View{
    public CanvasTestView(Context context) {
        this(context,null);
    }

    public CanvasTestView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CanvasTestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
}

绘制

  • onDraw() 用canvas画
  • 画直线lineTo
  • 画曲线,弧线,
  • 平移,缩放,旋转 save ,restore
  • 文字
  • invalidate();重走ondraw

生命周期方法

  • onSizeChanged :这个方法是在onMeasure方法执行后执行,最终的测量结果已经产生;可以用来获得屏幕的宽高然后计算要绘制的图像的位置

监听方法

  • onTouchEvent参数:触摸事件,return true 表示自己处理不分发了
    • 1,按下去;2,移动;3,抬起

自定义viewGroup(可以放子视图)

  • onLayout 可以先向group里addview,然后拿到孩子视图进行layout摆放

自定义view的制作

全部都可以画出来的,就是单个的view,不用measure和layout,步骤:

  • 自己初始好paint ,path
  • 计算好位置
  • 用canvas 画
  • 用onTouchEvent 来确定 事件并处理
    • event.getAction()获取行为MotionEvent.ACTION_DOWN,ACTION_MOVE,ACTION_UP
    • 用event.getX(), event.getY()获取位置
      • 与getRawY的区别是前者是距离控件,后者是距离屏幕(包含状态栏,就是整个手机,但是view操作的view里的坐标,所以获取了距离之后还要减掉状态栏的高度)
//获取状态栏高度
	private int getStatusBarHeight(View view) {
		Rect rect = new Rect();
		//获取视图对应的可视范围,会把视图的左上右下的数据传入到一个矩形中
		view.getWindowVisibleDisplayFrame(rect);
		return rect.top;
	}
  • 这里对比onClicklistener是针对单个控件的操作,想对控件内部的细节操作用onTouchEvent 处理;而且onTouchEvent 是在view的内部处理,onClicklistener要有使用view的组件来设定

位置摆放规则的viewgroup可以直接在xml中定义好各个子view的布局然后继承现成的viewgroup,如:LinearLayout,步骤:

  • 定义自定义viewGroup的视图(由于是规则的,所以可以写死在xml中)
  • 完成自定义viewGroup的监听事件,如popwindow的弹出;并完成popwindow的视图
  • 需要的数据有使用这个控件的,如activity 在onCreate方法里为控件设置,要知道activity的onResume方法完毕,才会对activity里的view进行measure和layout

针对位置摆放不规则的viewGroup,继承原生的viewgroup,里面的子view要手动处理

  • 定义好子控件的xml
  • 使用控件的,如:activity,把需要的数据,图片资源为控件设置好。
  • onMeasure()方法:遍历每个孩子,用getchildcount和getchildat,对孩子measure测量测量传的是尺寸的描述heightMeasureSpec
    • 在VIewGroup中系统默认只处理当前控件,不对子视图测量,要自己重写这个方法,步骤
  • onLayout()方法:遍历每个孩子,用getchildcount和getchildat,通过测量大小getMeasuredWidth,对孩子layout摆放

onMeasure方法学习(默认的super.onMeasure完成不了需要,1.只测了自己不测孩子,2.测量自己不能达到需求,有时超过了屏幕的限制)

  • MeasureSpec测量描述值
  • measure(MeasureSpec)对控件测量,最终调用setMeasuredDimension
  • setMeasuredDimension设置最终的测量宽高,一定要走
    • 按照不同的模式来处理
  • 针对自己的测量分为:未指定,精确
    • 精确(matchparent):如果用户设定的值超大就会显示宽高不协调,所以要自己确定
      获取屏幕宽高最小值
private int getDefaultWidth() {
		//获取屏幕宽高
		DisplayMetrics outMetrics = getResources().getDisplayMetrics();
		int width = outMetrics.widthPixels;
		int height = outMetrics.heightPixels;
		//获取宽度和高度的较小值
		int result = Math.min(width, height);
		return result;
	}
  • 未指定(wrapcontent):当布局里没有孩子,或者孩子是动态添加返回值就是0,这样图像就不能显示
  • getSuggestedMinimumWidth
protected int getSuggestedMinimumWidth() {
    //如果没有给View设置背景,那么就返回View本身的最小宽度mMinWidth
    //如果给View设置了背景,那么就取View本身最小宽度mMinWidth和背景的最小宽度的最大值
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
后面子view的大小要更加父view的测量尺寸,同步改变

自定义组合控件

  • 布局要善于利用include的思路,但是在include中不能设置属性
  • 所以引出自定义组合控件,在控件中把视图填充,然后设置属性
    • 注意inflate,第三参为
      • null:返回的view是本身,不带根部就参数,此时用addview加入根布局的话是不带根布局参数,要自己布局。
      • this:就是把view带着根布局参数填充到控件当中
  • 要自定义要填充的view的类型,那么可以使用自定义属性。
  • 示例
<resources>

    <declare-styleable name="SlidingMenu">
        <attr name="touchModeBehind">
            <enum name="margin" value="0" />
            <enum name="fullscreen" value="1" />
            <enum name="none" value="2" />
        </attr>
        <attr name="shadowDrawable" format="reference" />
    </declare-styleable>

</resources>
  • 然后在自定义view里获取自定义属性值集合TypeArray存,用完要recycle回收
  • 总结:自定义组合控件就是把控件封装到一个viewgroup里面,对子view的设置通过xml文件,和暴露给外面的方法灵活处理

有关值动画(ValueAnimator):当动画的执行不好直接处理,可以采用值动画,通过监听变化的值,不断地重绘图形。对应的是ObjectAnimator(要指定动画执行的object(view))

事件分发

  • 四个方法:dispatchtouchevent,ontouchevent(想吃),onintercepttouchevent(特别想吃),requestdisallowintercepttouchevent(别抢)
  • 事件是从外向内(先让大家都知道这个事),再从内向外传递,默认都不处理交给最外面的activity的ontouchevent处理
    • 事件优先交个子视图
    • 除非自己一定要处理这件事,否则不会拦截
  • 所有的布局最外层都是Framelayout
  • down事件确定目标target

onTouch onClick 是不同的 前者要按下移动;如果一个控件重写了onTouchEvent 返回True,且设置onTouch监听返回True,事件交给监听处理,监听优先级高。

WindowManager:这个类可以在任何的界面情况下添加一个额外的视图

manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
gooView = new GooView(context);
gooView.setOnGooViewChangeListener(this);
params = new WindowManager.LayoutParams();
//宽度和高度是
params.height = WindowManager.LayoutParams.WRAP_CONTENT
params.width = WindowManager.LayoutParams.WRAP_CONTENT
params.format = PixelFormat.TRANSLUCENT;//类型是透明
manager.addView(gooView, params);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值