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

一、自定义控件(一) --- 自定义属性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的背景修改自定义控件

效果如图:

这是源码哦

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

时间: 2024-08-28 15:56:45

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

Android自定义控件之轮播图控件

背景 最近要做一个轮播图的效果,网上看了几篇文章,基本上都能找到实现,效果还挺不错,但是在写的时候感觉每次都要单独去重新在Activity里写一堆代码.于是自己封装了一下.本篇轮播图实现原理原文出处:循环广告位组件的实现,这里只是做了下封装成一个控件,不必每次重复写代码了. 效果图 实现分析 轮播图的功能就是实现左右滑动的广告.图片信息展示,那我们就用ViewPager来实现,由于考虑到用户体验,我们还需要在下面加一个指示器来标示滑动到了第几张轮播图.指示器我们可以用一个线性布局来根据要展示的轮

android自定义控件之飞入飞出控件

最近呢,本人辞职了,在找工作期间,不幸碰到了这个求职淡季,另外还是大学生毕业求职的高峰期,简历发了无数份却都石沉大海,宝宝心里那是一个苦啊!翻着过去的代码,本人偶然找到了一个有意思的控件,那时本人还没有写博客的习惯,现在补上,先看效果图: 然后看用法代码: StellarMap stellarMap = (StellarMap) findViewById(R.id.stellar); // 设置数据 RecommendAdapter adapter = new RecommendAdapter(

Android自定义控件(一)——开关控件

Google 在 API 14 开始才新增了Switch 控件. 因此,我们可以选择自己封装一个Switch . 效果如图: View主要代码: [java] view plaincopy public class SwitchView extends LinearLayout { private ImageView maskImage;              // 开关遮盖图片 private boolean open;                     // 开关当前状态 priv

C#GDI+自定义绘制曲线图表控件DataChart 简单实现

C#GDI+自定义绘制曲线图表控件DataChart 这里只说明在计算刻度和曲线绘制的时候 只提供思路,只是做了下简单的计算,不喜勿喷 还望见谅,高手直接飘过吧.这个要做好,还是需要研究研究算法的,比如刻度随着控件的大小发生改变的时候计算不同的值,根据刻度范围来计算刻度以及刻度值等,这里没有研究,制作简单的绘制,让其知道自定义曲线控件的画法,对于新手来讲应该是有一些帮助的.有时间我把研究过后的算法加上做一个通用可靠一点的控件分享. 例子如下图所示 首先百度一张图片,就按照它的样子来绘制 该图片链

Android应用层View绘制流程与源码分析

Android应用层View绘制流程与源码分析 1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原理,记不记得最终分析结果就是下面的关系: 看见没有,如上图中id为content的内容就是整个View树的结构,所以对每个具体View对象的操作,其实就是个递归的实现. 前面<Android触摸屏事件派发机制详解与源码分析一(View篇)>文章的3-1

Android视图的绘制流程(下)——View的Layout与Draw过程

综述 在上篇文章中Android视图的绘制流程(上)--View的测量对View的Measure过程进行了详细的说明.对于在View的绘制的整个过程中,在对View的大小进行测量以后,便开始确定View的位置并且将其绘制到屏幕上.也就是View的Layout与Draw过程.那么就来看一下是如何实现这两个过程的. View的Layout过程 上文提到View的绘制流程是从ViewRoot的performTraversals方法开始,那么在View完成测量以后,在performTraversals方

Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框

转载请注明出处http://blog.csdn.net/xiaanming/article/details/11066685 今天给大家带来一个很实用的小控件ClearEditText,就是在Android系统的输入框右边加入一个小图标,点击小图标可以清除输入框里面的内容,IOS上面直接设置某个属性就可以实现这一功能,但是Android原生EditText不具备此功能,所以要想实现这一功能我们需要重写EditText,接下来就带大家来实现这一小小的功能 我们知道,我们可以为我们的输入框在上下左右

(转载) Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框

Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框 标签: Android清除功能EditText仿IOS的输入框 2013-09-04 17:33 70865人阅读 评论(57) 收藏 举报  分类: Android UI设计(7)  版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请注明出处http://blog.csdn.net/xiaanming/article/details/11066685 今天给大家带来一个很实用的小控件ClearEdit

【读书笔记-《Android游戏编程之从零开始》】6.Android 游戏开发常用的系统控件(TabHost、ListView)

3.9 TabSpec与TabHost TabHost类官方文档地址:http://developer.android.com/reference/android/widget/TabHost.html Android 实现tab视图有2种方法,一种是在布局页面中定义<tabhost>标签,另一种就是继承tabactivity.但是我比较喜欢第二种方式,应为如果页面比较复杂的话你的XML文件会写得比较庞大,用第二种方式XML页面相对要简洁得多. <?xml version="1