Android自定义控件 ----- 基本绘制流程,简单控件的实现

来源:互联网 发布:推女郎吧最新域名 编辑:程序博客网 时间:2024/04/27 14:55
一、自定义控件(一) --- 自定义属性TextView
1,定义属性,制作attrs.xml文件;
    属性值:
string,color,attr,array,bool,declare-styleable,dimen,drawable,eat-comment,fraction,
integer,integer-array,item,plurals,string-array,style
    属性取值范围:
string,color,demension,integer,enum,reference,float,boolean,fraction,flag;
<?xml version="1.0" encoding="utf-8"?><resources>    <!--CustomTitleView-->    <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>

    <!--属性值具体意义详述:            reference:参考某一资源ID            color:颜色值            boolean:布尔值            dimension:尺寸值            float:浮点值            integer:整型值            string:字符串            fraction:百分数            enum:枚举值            flag:位或运算            多类型:                <declare-styleable name = "名称">                <attr name = "background" format = "reference|color" />                </declare-styleable>    -->

2,重写构造方法【1,2,3参数,重写三参数】
    获取View属性值,实现基本的布局
    /**     * 文本     */    private String mTitleText;    /**     * 文本的颜色     */    private int mTitleTextColor;    /**     * 文本的大小     */    private int mTitleTextSize;    /**     * 绘制时控制文本绘制的范围     */    private Rect mBound;    /**     * 画笔     */    private Paint mPaint;    /**     * 构造方法     *     * @param context     * @param attrs     */    public CustomTitleView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    /**     * 构造方法     *     * @param context     */    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);        /**         * 添加事件【<span style="color:#333399;">第五步时,把这一块代码加入</span>】         */        this.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                mTitleText = randomText();                postInvalidate();            }        });        /**         * 获得我们所定义的自定义样式属性         */        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,重写onDraw()方法,绘制View
    paint绘制view到canvas上【将view用笔绘制到画布上】
    @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);    }

4,重写onMeasure()方法
    实现将所有View重新布局【摆放】
    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        /**         * 1,不重写该方法,系统默认填充父窗体         *///        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        /**         * 2, 重写当前方法         */        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(mTitleText, 0, mTitleText.length(), mBound);            float textWidth = mBound.width();            int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());            width = desired;        }        /**         * 高度获取         */        if (heightMode == MeasureSpec.EXACTLY) {            height = heightSize;        } else {            mPaint.setTextSize(mTitleTextSize);            mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);            float textHeight = mBound.height();            int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());            height = desired;        }        setMeasuredDimension(width, height);    }

5,添加控件的响应事件
    添加事件触发器
    实现事件的具体响应
    /**     * 生成随机文字【也可以实现其他的事件,管理控件属性】     *     * @return     */    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();    }


在使用命名空间时:
xmlns:test="http://schemas.android.com/apk/res-auto"  在AS中使用
xmlns:test="http://schemas.android.com/apk/res/[你的包名]"

http://www.imooc.com/video/8302  慕课网【推荐学习】

二、自定义控件(二) --- 属性TextView联合图片

自定义控件的基本步骤:
1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
[ 3、重写onMesure ]  //可选项
4、重写onDraw


在绘制过程中,控制内容大小。

扩展:
        为自定义控件添加触发事件;
        为自定义控件添加整体响应【事件机制,返回,Home处理】
   <!--CustomImageView-->    <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>
/** * 类说明:带图片说明的ImageView控件 * 作者:vision * 时间:2016/7/15 */public class CustomImageView extends View {    /**     * 标识图片当前缩放模式     */    private static final int IMAGE_SCALE_FITXY = 0;    /**     * 文本     */    private String mTitle;    /**     * 文本的颜色     */    private int mTextColor;    /**     * 文本的大小     */    private int mTextSize;    /**     * 缩放参数     */    private int mImageScale;    /**     * 绘制时控制文本绘制的范围     */    private Rect mTextBound;    /**     * 绘制整体的范围     */    private Rect rect;    /**     * 画笔     */    private Paint mPaint;    /**     * 图像内容     */    private Bitmap mImage;    /**     * 控件宽度     */    private int mWidth;    /**     * 控件高度     */    private int mHeight;    /**     * 响应事件监听器     */    private CustomImageViewClickListener listener;    /**     * 构造方法     *     * @param context     * @param attrs     */    public CustomImageView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    /**     * 构造方法     *     * @param context     */    public CustomImageView(Context context) {        this(context, null);    }    /**     * 初始化所特有自定义类型     *     * @param context     * @param attrs     * @param defStyle     */    public CustomImageView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        this.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View view) {                if (listener != null) {                    listener.onCustomImageViewClickListener(view);                }            }        });        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);    }    @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);    }    @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(Paint.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);        }    }    /**     * 设置监听器     *     * @param listener     */    public void setListener(CustomImageViewClickListener listener) {        this.listener = listener;    }    public interface CustomImageViewClickListener {        void onCustomImageViewClickListener(View v);    }}

三、使用自定义控件

    <future.com.selfdefineviewfirst.view.CustomTitleView        android:id="@+id/text_self_define"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        app:titleText="自定义控件属性:标题文字"        app:titleTextColor="@color/green"        app:titleTextSize="18sp" />
    <future.com.selfdefineviewfirst.view.CustomImageView        android:id="@+id/pengyuyan1"        android:layout_width="150dp"        android:layout_height="wrap_content"        selfde:image="@mipmap/a7"        selfde:imageScaleType="center"        selfde:titleText="彭于晏最棒最帅最好的潜力股"        selfde:titleTextColor="#0000ff"        selfde:titleTextSize="30sp" />
public class MainActivity extends AppCompatActivity implements CustomView.CustomListener {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ((CustomView) this.findViewById(R.id.custom1)).setCustomListener(this);    }    @Override    public void onCuscomClick(View v, int custom_id) {        switch (custom_id) {            case 1:                Toast.makeText(this, "你点我干嘛?!", Toast.LENGTH_LONG).show();                break;            default:                break;        }    }
/** * 类说明:CustomImageView 展示页面 * 增加:控件点击事件 * <p/> * 作者:vision * 时间:2016/7/15 */public class CustomImageViewActivity extends Activity implements CustomImageView.CustomImageViewClickListener {    /**     * 第一个控件     */    private CustomImageView pengyuyan1;    /**     * 第二个控件     */    private CustomImageView pengyuyan2;    /**     * 第三个控件     */    private CustomImageView pengyuyan3;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_custom_image_view);        pengyuyan1 = (CustomImageView) findViewById(R.id.pengyuyan1);        pengyuyan2 = (CustomImageView) findViewById(R.id.pengyuyan2);        pengyuyan3 = (CustomImageView) findViewById(R.id.pengyuyan3);        pengyuyan1.setListener(this);        pengyuyan2.setListener(this);        pengyuyan3.setListener(this);    }    @Override    public void onCustomImageViewClickListener(View v) {        switch (v.getId()) {            case R.id.pengyuyan1:                Toast.makeText(this, "帅哥彭于晏1", Toast.LENGTH_LONG).show();                break;            case R.id.pengyuyan2:                Toast.makeText(this, "德艺双馨彭于晏2", Toast.LENGTH_LONG).show();                break;            case R.id.pengyuyan3:                Toast.makeText(this, "超越自己彭于晏3", Toast.LENGTH_LONG).show();                break;        }    }}



四、相关类似功能的扩展
实现加载过程进度显示
TextView的背景修改自定义控件
效果如图:


这是源码哦




有些时候,我们自以为我们不配得到的,其实是我们错过的;而配不上我们的,还放肆的将我们辜负了。

0 0
原创粉丝点击