分享好友 移动开发首页 频道列表

Android 自定义 View(基础)

Android开发  2016-12-01 11:170

一:引言

Android的开发中,在移动设备上展示的所有内容,都是靠一个一个具体的视图控件,按照一定的排列规则展示出来的,这些一个个控件,都是系统提供给我们的。但是我们看到,app商店上有些比较炫酷的页面展示,我们会发现,系统根本没有提供那些控件,那么这是怎么实现的呢?对就是通过我们的自定义控件去完成。那么什么是自定义控件呢,这里我个人理解可以分为三类:

  1. 自定义试图,——继承 View,然后自绘试图内容
  2. 自定义组合试图,——继承ViewGroup,然后对子类试图进行重新布局。
  3. 自定义已有试图,——继承已有的View,比如继承ImageView

    自定义控件是android修行道路上的必经之路,也是得道升仙的必备能力。所以我们的跨过去,每天练习一点点,就一点点。

二:自定义View步骤

这里介绍下自定义试图的主要步骤

- 自定义属相

- 继承View重写构造方法

- 获取自定义属性

- 重写测量控件的宽高

- 绘制控件显示

- 提供自定义事件

三:自定义View

  • 自定义属性

    自定义属性一共有10中定义类型,String,boolean等,具体的类型

    和使用对应如下代码

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="text" format="string"></attr>
    <!--
    定义:资源ID
    使用:@drawable/图片ID
    -->
    <attr name="msrc" format="reference"></attr>
    <!--
    定义: 颜色值
    使用: android:mcolor = "#00FF00"
    -->
    <attr name="mcolor" format="color"></attr>
    <!--
    定义:布尔类型
     使用:android:misfocus = "true"
    -->
    <attr name="misfocus" format="boolean"></attr>
    <!--
    定义:尺寸
    使用: android:msize = "42dip"
    -->
    <attr name="msize" format="dimension"></attr>
    <!--
    定义:浮点值
    使用: android:malpha = "0.1"
    -->
    <attr name="malpha" format="float"></attr>
    <!--
    定义:整形
    使用:android:mcount = "12"
    -->
    <attr name="mcount" format="integer"></attr>
    <!--
    定义:字符串
    使用:android:apiKey = "2223"
    -->
    <attr name="apikey" format="string"></attr>
    <!--
    定义:百分数
    使用:100%
    -->
    <attr name="mcurrent" format="fraction"></attr>
    <!--
    定义:枚举

    使用:type:1
    -->
    <attr name="type">
        <enum name="cycle" value="1"></enum>
        <enum name="round" value="2"></enum>
    </attr>
    <declare-styleable name="customView">
        <attr name="text"/>
        <attr name="mcolor"/>
        <attr name="msize"/>
    </declare-styleable>
</resources>
  • 编写自定义控件,使用自定义属性
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    >

    <com.zhang.zs.customviewdemo.MyCuntomView
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp"
        app:mcolor="#ff0"
        app:msize="18sp"
        app:text="你好" />
</RelativeLayout>
  • 创建自定义View继承View(重写构造方法)

    在创建View的时候,需要重写构造方法,一般重写前三个构造方法就可以了,但是如果我们的自定控件是通过布局文件的形式加载,则第二个构造必须重写,不然会报错。

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

    public MyCuntomView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

        //获取自定义属性
        initViewAtrr(context, attrs, defStyleAttr);
    }
  • 获取自定属性的值

    在获取自定义属性值的时候,我们通过循环的方式来获取值,这样获取到属性值,就是我们xml文件中使用到的,没有使用到的就获取不到。而并获取我们所有自定义的属性。

private void initViewAtrr(Context context, AttributeSet attrs, int defStyleAttr) {

        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.customView, defStyleAttr, 0);

        //获取有几个自定义属相
        final int count = a.getIndexCount();
        Log.e("TAG", "=====" + count);

        for (int i = 0; i < count; i++) {
            int type = a.getIndex(i);
            switch (type) {
                case R.styleable.customView_text:

                    text = a.getString(type);
                    if (TextUtils.isEmpty(text)) {
                        text = "我是文本";
                    }
                    break;

                case R.styleable.customView_mcolor:

                    corlor = a.getColor(type, Color.RED);

                    break;

                case R.styleable.customView_msize:

                    msize = a.getDimensionPixelSize(type, 15);
                    break;


            }

        }

        a.recycle();

        paint = new Paint();
        //抗锯齿
        paint.setAntiAlias(true);


    }
  • 测量控件的大小(重写onMeasure方法)

    测量之前先了解MeasureSpec的specMode,mode共有三种情况,取值分别为MeasureSpec.UNSPECIFIED, MeasureSpec.EXACTLY, MeasureSpec.AT_MOST。

MeasureSpec.EXACTLY是精确尺寸,当我们将控件的layout_width或layout_height指定为具体数值时如andorid:layout_width=”50dip”,或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。

MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。

MeasureSpec.UNSPECIFIED是未指定尺寸,这种情况不多,一般都是父控件是AdapterView,通过measure方法传入的模式。

知道以上概念之后我们重写测量就容易的多了

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

        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int size = MeasureSpec.getSize(widthMeasureSpec);
        bounds = new Rect();
        if (mode == MeasureSpec.EXACTLY) {
            mwidth = size;
        } else {

            paint.setTextSize(msize);

            paint.getTextBounds(text, 0, text.length(), bounds);
            mwidth = getPaddingLeft() + getPaddingRight() + bounds.width();

        }


        mode = MeasureSpec.getMode(heightMeasureSpec);
        size = MeasureSpec.getSize(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {

            mheight = size;
        } else {

            paint.getTextBounds(text, 0, text.length(), bounds);
            mheight = getPaddingBottom() + getPaddingTop() + bounds.height();
        }

      r=Math.max(mwidth,mheight);
        setMeasuredDimension(r, r);

    }
  • 绘制控件显示(重写onDraw方法)
@Override
    protected void onDraw(Canvas canvas) {
        paint.setColor(corlor);
        canvas.drawCircle(r/2,r/2,r/2,paint);
        paint.setColor(Color.BLACK);
        canvas.drawText(text,r/2-bounds.width()/2,r/2+bounds.height()/2,paint);

    }

 Android 自定义 View(基础)

  • 定义事件
@Override
    public boolean onTouchEvent(MotionEvent event) {
        if(event.getAction()==MotionEvent.ACTION_DOWN){
            if(changeColor!=null){
                changeColor.change(this,text);
            }

        }
        return super.onTouchEvent(event);
    }


    public interface  ChangeColor{

        public void change(MyCuntomView  view,String name);
    }

    public ChangeColor changeColor;

    public void setChangeColor(ChangeColor changeColor) {
        this.changeColor = changeColor;
    }

MainActivity代码

public class MainActivity extends AppCompatActivity {
    private MyCuntomView name;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);



        name = (MyCuntomView) findViewById(R.id.name);
        name.setChangeColor(new MyCuntomView.ChangeColor() {
            @Override
            public void change(MyCuntomView view,String text) {

                Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show();

            }
        });

    }

以上就是自定义View的整个流程。

查看更多关于【Android开发】的文章

展开全文
相关推荐
反对 0
举报 0
评论 0
图文资讯
热门推荐
优选好物
更多热点专题
更多推荐文章
Supporting Multiple Screens
术语和概念Screen size 屏幕尺寸又称「屏幕大小」,是屏幕对角线的物理尺寸。单位英寸 inch,比如 Samsung Note4 是 5.7 英寸。Resolution 屏幕分辨率屏幕纵横方向上物理像素的总数,比如 Samsung Note4 是 2560x1440,表示纵向有 2560 个像素,横向有 1440

0评论2017-02-05363

Android插件化(4):OpenAtlasの插件的卸载与更新
如果看过我的前两篇博客Android插件化(2):OpenAtlas插件安装过程分析和Android插件化(3):OpenAtlas的插件重建以及使用时安装,就知道在插件的安装过程中OpenAtlas做了哪些事,那么插件的卸载就只需要把持久化和内存中的内容移除即可。1.插件的卸载插件卸载的

0评论2017-02-05229

个人简历
吴朝晖/男/1993.1本科/南京师范大学中北学院信息系工作年限:1年以内技术博客:wuzhaohui026.github.ioGitHub:https://github.com/wuzhaohui026期望职位:Android开发(初级Android工程师)期望薪资:税前月薪5.5k~7k期望城市:常州工作经历常州慧展信息科技有

0评论2017-02-05126

Android插件化(五):OpenAtlasの四大组件的Hack
引言到目前为止,我们已经分析了OpenAtlas中插件的安装,卸载,更新,以及安装好插件之后组件类的加载过程,但是对于这些是如何引发的还不知道,比如,在宿主的一个Activit中调用startActivity()跳转到插件中的一个Activity,如何判断这个Activity在的插件是否

0评论2017-02-0598

更多推荐