自定义控件(二)

多点触控事件简单介绍

最关键的是onTouchEvent这个方法明天应该就会继续介绍比这要更加多的于事件相关的,比如事件分发等,今天是简单的事件,不多说,如下:

单指 4个动作 + x值 和 y 值

  @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }

重写onTouchEvent就好了,这里面有好几个事件,准确说是一组事件分别是:

  1. MotionEvent.ACTION_DOWN 手指按下
  2. MotionEvent.ACTION_MOVE 手指移动
  3. ACTION_UP 手指抬起
  4. ACTION_CANCEL 取消,这里看来和up没什么区别,和事件分发有关,这里就不详细讲了,可能明天就学了(笑);

以上就是简单的介绍,接下来实现一个简单的案例 简单的画画板

package com.zhouzhou.flowlayoutdemo;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

/**
 * Created by zhousaito on 2016/9/7.
 */
public class TouchView extends View {

    private Paint mPaint;
    private Path mPath;
    private float mLastX;
    private float mLastY;
    private int mPenColor;
    private int mPenSize;

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

    public TouchView(Context context, AttributeSet attrs) {
        this(context, attrs, R.style.MyDefStyle);
    }

    public TouchView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TouchView, defStyleAttr, R.style.MyDefStyle);
        for (int i = 0; i < array.getIndexCount(); i++) {
            int index = array.getIndex(i);
            switch (index) {
                case R.styleable.TouchView_penColor:
                    mPenColor = array.getColor(index, Color.RED);
                    break;
                case R.styleable.TouchView_penSize:
                    mPenSize = array.getDimensionPixelSize(index, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()));
                    break;
            }
        }
        mPaint = new Paint();
        mPath = new Path();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mPenSize);
        mPaint.setColor(mPenColor);
        //画上线条
        canvas.drawPath(mPath,mPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = event.getX();
                mLastY = event.getY();
                mPath.moveTo(mLastX, mLastY);
                break;
            case MotionEvent.ACTION_MOVE:
                mPath.lineTo(event.getX(), event.getY());
                mLastX = event.getX();
                mLastY = event.getY();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                break;
        }
        //重写绘制
        invalidate();
        return true;
    }
}

这里写图片描述

这里加了一些属性和style
在 attrs.xml文件中定义

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--
    设置默认样式(style)
    这个 parent="android:Widget" 为了表示是一个属性
    这样就可以应用,可以不写
    -->
    <style name="MyDefStyle" parent="android:Widget">
        <item name="penColor">#ff0</item>
        <item name="penSize">5dp</item>
    </style>
    <declare-styleable name="TouchView">
        <attr name="penColor" format="color"/>
        <attr name="penSize" format="dimension"/>
    </declare-styleable>
</resources>

然后代码引用上就好了,这样,简单的画板就完成了,哈哈

接下来学一下 Matrix API 我没怎么搞懂,还是说一下吧,
用画图来解释吧,Matrix 矩阵,有几种方式
1.缩放,这个简单,就要知道mMatrix.postScale(0.5f,0.5f,100,100);
前面是缩放比例,后面是旋转中心点,不写就是绕着父控件的左上角缩放
2.旋转,mMatrix.postRotate(10,100,100); 和缩放差不多,
3.平移 mMatrix.postTranslate(10,10); 就是移动
4.倾斜 mMatrix.postSkew(0.1f,0);
上面4个属性简单,我就不写了,网上一大堆,其实学属性动画时候就有学到这些,就倾斜没有,然而这属性并不常用,其实我想上gif 图片的,感觉自己想傻吊一样,这上gif,太占资源吧,4个属性4个图

多手指触控事件

event.getActionMasked(); 并不是event.getAction(),而是通过event.getActionMasked()这值来判断,是否多手指的触控的

MotionEvent.ACTION_POINTER_DOWN 按下
MotionEvent.ACTION_MOVE 移动(和单手指一样的)
MotionEvent.ACTION_POINTER_UP 抬起

这样就可以做对应的事情了,
**[0] 主要和单手指冲突时,可以根据event.getPointerCount()
来判断多少个手指,然后做对应的事情**

继承ViewGroup 的自定义

今天是继承ViewGroup来实现自定义控件
FlowLayout(流式布局) 其实很简单,但是自己没那么有逻辑,先不废话了

    [1] TODO  --> onLayout() -->里面的值
    [2] 用recycleView来实现一个流式布局
    [3] 必须到onMeasure中写  measureChildren(); 才可以显示 不然不能显示

第一步 继承viewGroup

public class FlowLayout extends ViewGroup {
    public FlowLayout(Context context) {
        super(context);
    }

    public FlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

继承完之后,我们的目标是flowLayout 所以更具需求来写

 /**
     *       MeasureSpec.UNSPECIFIED
     *       --->子控件自己的大小来显示,所以怎么设置值都没什么用
     *       二级缓存绘制
     *    [1] TODO  --> onLayout() -->里面的值
     *    [2] 用recycleView来实现一个流式布局
     *    [3] 必须到onMeasure中写  measureChildren(); 才可以显示 不然不能显示
     * @param changed
     * @param l 父控件的位置
     * @param t
     * @param r
     * @param b
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //流式布局  从左边到右边开始排
        int left = l;
        int maxHeight =0;
        int top = t;
        //总宽度
        int width = r - l;

        for (int i = 0; i < getChildCount(); i++) {
            //得到子类控件
            View view = getChildAt(i);
//            Log.e("自定义标签", "类名==FlowLayout" + "方法名==onLayout=====:" );
            //
            if (left + view.getMeasuredWidth() > width) {
                top += maxHeight;
                maxHeight=0;
                left =l;
            }
            view.layout(left,top,left+view.getMeasuredWidth(),top+view.getMeasuredHeight());
            left+=view.getMeasuredWidth();
            maxHeight = Math.max(maxHeight, view.getMeasuredHeight());
        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /**
         * 要写这个才能显示
         */
//        measureChildren(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        measureChildren(widthMeasureSpec,heightMeasureSpec);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

这样大概实现了一下,因为没有重写onMeasure()方法,所以,flowLayout的宽和高不管用,都是满上了父控件(可以说match_parent,因为表现是充满的)
这里写图片描述

这样的话,如果给 flowlayout 加一个padding的话,就看出来,我们的工程并没有完成
先说好,布局是6个textView和6个机器人图标交错,就是几行复制的,哈哈

 <com.zhouzhou.flowlayoutdemo.FlowLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#00f"
        android:padding="50dp">
        <!--加了50的padding-->

这里写图片描述

就成这样了,发现是自己没有处理padding和margin

  @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int left = getPaddingLeft();
        int maxHeight = 0;
        int top = getPaddingTop();
        int width = r - l - getPaddingLeft() - getPaddingRight();
        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
            if (left + view.getMeasuredWidth() > width) {
                top += maxHeight;
                maxHeight = 0;
                left = getPaddingLeft();
            }
            view.layout(left + params.leftMargin,
                    top + params.topMargin,
                    left + params.leftMargin + view.getMeasuredWidth(),
                    top + params.topMargin + view.getMeasuredHeight());
            left += view.getMeasuredWidth() + params.leftMargin + params.rightMargin;
            maxHeight = Math.max(maxHeight, view.getMeasuredHeight() + params.topMargin + params.bottomMargin);
        }
    }

加了些padding的处理效果 (背景我去了,看着头晕(笑))

这里写图片描述

接下来写onMeasure() 方法,来决定 父控件自身的宽度,从而可以通过高和宽,来进行显示,这样,可以平常使用就差不多了,哈哈

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /**
         * 要写这个才能显示
         */
//        measureChildren(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width = 0;
        int height = 0;
        //只要不是固定的  就
        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
            int left = getPaddingLeft();
            int maxHeight = 0;
            int top = getPaddingTop();
            for (int i = 0; i < getChildCount(); i++) {
                View view = getChildAt(i);
                MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
                //相加后,通过原来的
                if (left + view.getMeasuredWidth() + params.rightMargin + params.leftMargin > widthSize - getPaddingRight()) {
                    top += maxHeight;
                    maxHeight = 0;
                    //在这里得到的是left的,相当于这排排满了的最大宽度
                    width = Math.max(width, left);
                    left = getPaddingLeft();
                }
                left += view.getMeasuredWidth() + params.rightMargin + params.leftMargin;
                maxHeight = Math.max(maxHeight, view.getMeasuredHeight() + params.topMargin + params.bottomMargin);
            }
            height = maxHeight + top + getPaddingTop() + getPaddingBottom();
            switch (widthMode) {
                case MeasureSpec.AT_MOST:
                    width = Math.min(width,widthSize);
                    break;
                case MeasureSpec.UNSPECIFIED:
                    width = widthSize;
                    break;
            }
            switch (heightMode) {
                case MeasureSpec.AT_MOST:
                    //容器的高度
                    height = Math.min(heightSize, height);
                    break;
                case MeasureSpec.UNSPECIFIED:
                    //容器的高度

                    height = heightSize;
                    break;
            }
        }else {
            //固定的就用自己固定的高和宽
            width =widthSize;
            height = heightSize;
        }

        setMeasuredDimension(width, height);
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

写完这些,哈哈,基本完成了,简单的继承了viewGroup然后,自己写了一个简单的容器,flowlayout,效果如下

这里写图片描述

结果,还是比较丑,哈哈

以上就今天的学习类容,哈哈,又写了2 小时多,努力努力!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值