初探自定义控件

来源:互联网 发布:布里斯班 知乎 编辑:程序博客网 时间:2024/05/16 07:36

作为移动开发,自定义控件必不可少,之前大多时候用的自定义控件都是在网上复制粘贴的,并没有仔细琢磨,趁着有时间,尝试自己写写看,并把遇到的问题总结一下!

一般来说把自定义控件分为3中:1、自绘控件(自己绘制各种图形) 2、组合控件(系统已有控件组合到一起) 3、继承控件(对系统已有控件功能扩展)

首先了解view绘制的三个主要流程,Measure----Layout----Draw

View先测量大小,对应的方法是onMeasure();设置子控件的位置,对应方法是onLayout();绘制图形,对应的方法是onDraw();

一、自绘控件

绘制一个简单的图形: 一个太极图标

首先新建类继承View,重写起构造方法,与我们紧密相关的主要有三个方法:onMeasure(),draw(),onTouchEvent();

在onMeasure方法中主要绘制控件的大小,在这个方法中可以设置空间的大小,其中需要注意的是MeasureSpec.getSize()方法,可以获取控件的大小。

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, widthMeasureSpec);//设置宽度与高度相等    viewWidth=MeasureSpec.getSize(widthMeasureSpec);//控件的宽度  指的是像素    Log.e("myview", "onMeasure: ----size----" + MeasureSpec.getSize(widthMeasureSpec)            + "----mode---" + MeasureSpec.getMode(widthMeasureSpec));}

在draw()方法中绘制图形,绘制图形主要有Paint(画笔),Canvas(画布)两个类完成的:

有两种黑白颜色的画笔,定义一个创建得到画笔的方法:

private Paint getPaint(int color){    Paint paint=new Paint();    paint=new Paint();    paint.setAntiAlias(true);////设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。    paint.setDither(true);//设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰    /*    * 置画笔样式,如果不设置,默认是全部填充(FILL)。可选项为:FILLFILL_OR_STROKE,或STROKE        画笔样式分三种:            1.Paint.Style.STROKE:描边            2.Paint.Style.FILL_AND_STROKE:描边并填充            3.Paint.Style.FILL:填充    * */    paint.setStyle(Paint.Style.FILL_AND_STROKE);    paint.setColor(color);    return paint;}
然后再onDraw()方法中绘制图形

@Overridepublic void draw(Canvas canvas) {    super.draw(canvas);    int srccnWidth=viewWidth;    //控制椭圆大小及位置的矩形坐标  对应的是左上 右下坐标  这里表示的一个正方形  绘制出来的是一个圆形圆弧    RectF rect=new RectF(0,0,srccnWidth,srccnWidth);    //左侧黑色圆弧    canvas.drawArc(rect,90,180,false,mArcPaintBlack);//false 表示只绘制一个圆弧   true表示 圆弧将会闭合 会多出来一条线    //右侧白色圆弧    canvas.drawArc(rect,-90,180,false,mArcPaintWhite);    //上方黑色小圆弧    RectF rect2=new RectF(srccnWidth/4,0,srccnWidth/4*3,srccnWidth/2);    canvas.drawArc(rect2,-90,180,false,mArcPaintBlack);    //下方白色小圆弧    RectF rect3=new RectF(srccnWidth/4,srccnWidth/2,srccnWidth/4*3,srccnWidth);    canvas.drawArc(rect3,90,180,false,mArcPaintWhite);    //黑色圆圈    canvas.drawCircle(srccnWidth/2,srccnWidth/4,srccnWidth/16,mArcPaintWhite);    //白色圆圈    canvas.drawCircle(srccnWidth/2,srccnWidth/4*3,srccnWidth/16,mArcPaintBlack);}

在自定义控件中添加自定义的属性(如控制画笔的颜色):

在valus目录下创建attrs.xml文件

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="myView">        <attr name="lbb_painColor" format="color|reference"/>        <attr name="lbb_lineWeigh" format="dimension"/>    </declare-styleable></resources>
name="lbb_viewbg"   自定义属性名字    format="color|reference"   属性类型

format各种值的含义:

reference:引用资源
string:字符串
Color:颜色
boolean:布尔值
dimension:尺寸值
float:浮点型
integer:整型
fraction:百分数
enum:枚举类型
flag:位或运算

在自定义控件中获取属性:

public MyView2(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    getProperties(attrs);//在构造方法执行}//获取自定义属性的值private void getProperties(AttributeSet attrs){    TypedArray typedArray=getContext().obtainStyledAttributes(attrs, R.styleable.myView);    colorWhite=typedArray.getColor(R.styleable.myView_lbb_painColor, colorWhite);//获取画笔颜色 属性    mLineStrokeWidth=typedArray.getDimension(R.styleable.myView_lbb_lineWeigh, mLineStrokeWidth);//获取画笔大小    typedArray.recycle();        mArcPaintBlack=getPaint(colorBlack);    mArcPaintWhite=getPaint(colorWhite);//给黑色画笔设置颜色}

在xml中定义属性:

<LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:lbb="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:layout_margin="20dp"    >    <ScrollView        android:layout_width="match_parent"        android:layout_height="wrap_content">        <LinearLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            android:orientation="vertical"            >            <com.example.teemo.myview_demo.myView.MyView2                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:background="#00ff00"                lbb:lbb_painColor="#ffff00"                />            <View                android:layout_width="match_parent"                android:layout_height="20dp"/>            <com.example.teemo.myview_demo.myView.MyView1                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:background="#ff0000"                />        </LinearLayout>    </ScrollView></LinearLayout>

在布局文件中根控件中添加

xmlns:lbb="http://schemas.android.com/apk/res-auto"  其中lbb自己任意定义  据说添加这个后Gradle自动查找自定义的属性(没有深入研究)

在自定义控件MyView2中添加属性

lbb:lbb_painColor="#ffff00"   这时白色的画笔会变为我们设置的颜色

另外有有一点,当绘制线条时,如果线条很粗,要考虑我们定义的画笔开始/结束的坐标位于线条的中心,如当我们定义的线条宽度为80f时,在空间左边缘竖直画一条线时,其宽度只有40f,

以为还有一半超出了控件边界。如

canvas.drawLine(0,0,0,mLineStrokeWidth,mLinePaint);

可以改为 canvas.drawLine(40,0,40,mLineStrokeWidth,mLinePaint);

这个是画万字符号时遇到的问题

二 、组合控件

组合控件只要把系统已有的控件组合起来即可,实现相对复杂的布局,也可以把常用到的布局封装起来。

以linearlayout 中嵌套  TextView  和 Recyclerview  为例,其效果如下

首先定义一个类在其内部实现布局:

/** * Created by lianbinbo on 2016/12/20. */public class MyViewHolder extends RelativeLayout {    private Context mContext;    private RecyclerView mRecyclerView;    private TextView mTextView;    private Adapter_view3 mAdapter_view3;    public MyViewHolder(Context context) {        super(context);        this.mContext=context;        initView();    }    private void initView(){        View view= LayoutInflater.from(mContext).inflate(R.layout.myview3,this);//要实现的布局        mRecyclerView= (RecyclerView) view.findViewById(R.id.rv_item);        mTextView= (TextView) view.findViewById(R.id.tv_title);        GridLayoutManager gridLayoutManager=new GridLayoutManager(mContext,2);//实现recycleview为网格形式        if (mAdapter_view3==null){            mAdapter_view3=new Adapter_view3(mContext);        }        if (mRecyclerView!=null){            mRecyclerView.setLayoutManager(gridLayoutManager);            mRecyclerView.setHasFixedSize(true);            mRecyclerView.setAdapter(mAdapter_view3);        }    }    public void addItem(List<ItemModle> itemModle,int pos){//得到数据        if (mAdapter_view3!=null){            mAdapter_view3.updateItems(itemModle);//更新界面            Log.e("myView3", "myViewHolder: -----" + itemModle.size() + "---pos---" + pos);        }        if (mTextView!=null){//如果要增加列表 直接增加数据 在这里修改列表头 在适配器 判断显示不同数据            if (pos==0){                mTextView.setText("质态");            }else {                mTextView.setText("资源");            }        }    }}

该demo使用该自定义布局是在Recycleview中,在一个Activity中定义一个RecyclerView,然后把该自定义控件加载到Recycleview中,可以实现上面的排列效果,如果要增加排列的数量,直接控制数据就可以了。

Activity 中布局:

<RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.example.teemo.myview_demo.Main2Activity">    <android.support.v7.widget.RecyclerView        android:id="@+id/rv_main2Activity"        android:layout_width="match_parent"        android:layout_height="match_parent"/></RelativeLayout>

Activity中RecyclerView中的适配器:

/** * Created by lianbinbo on 2016/12/20. */public class MyAdapter_main2 extends BaseRecyclerAdapter<MyModle1> {    public MyAdapter_main2(Context mContext) {        super(mContext);    }    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        RecyclerView.ViewHolder viewHolder;        viewHolder=new ViewHolderMain2(new MyViewHolder(mContext));        return viewHolder;    }    @Override    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {        ViewHolderMain2 viewHolderMain2= (ViewHolderMain2) holder;        //把数据传到定义的控件中        viewHolderMain2.myViewHolder.addItem(mItem.get(position).getItemModleList(),position);        Log.e("myView3", "onBindViewHolder--1--: --position---"+position+"---size---"+mItem.size() );    }    public class ViewHolderMain2 extends RecyclerView.ViewHolder{        MyViewHolder myViewHolder;//自己定义的控件        public ViewHolderMain2(View itemView) {            super(itemView);            myViewHolder= (MyViewHolder) itemView;        }    }}

自定义控件中的适配器:

/** * Created by lianbinbo on 2016/12/20. */public class Adapter_view3 extends BaseRecyclerAdapter<ItemModle> {    public Adapter_view3(Context mContext) {        super(mContext);    }    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        return new ViewHolderView3(LayoutInflater.from(mContext).inflate(R.layout.myview3_item,parent,false));    }    @Override    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {        ViewHolderView3 viewHolderView3= (ViewHolderView3) holder;        ItemModle itemModle=getItem(position);        if ("0".equals(itemModle.getTag())){//根据  传入的标记  显示不同的图片            viewHolderView3.iv.setImageResource(R.drawable.home_grid_04);        }else if ("1".equals(itemModle.getTag())){            viewHolderView3.iv.setImageResource(R.drawable.home_grid_06);        }        viewHolderView3.textView.setText(itemModle.getName());        Log.e("myView3", "onBindViewHolder--2--: ---position--" + position + "---size---" + mItem.size());    }    public class ViewHolderView3 extends RecyclerView.ViewHolder{        private ImageView iv;        private TextView textView;        public ViewHolderView3(View itemView) {            super(itemView);            iv= (ImageView) itemView.findViewById(R.id.iv);            textView= (TextView) itemView.findViewById(R.id.tv_item);        }    }}

这样把控件封装到一起,有类似的布局就可以复用:而且在Recycleview中用该控件,可以轻松实现有连续相似布局的效果。像一些视频APP节目的展示:

三,继承控件,这个不多说了,直接在网上复制一个代码,实现禁止GridView滑动的效果

public class GridViewInScrollView extends GridView {    public GridViewInScrollView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public GridViewInScrollView(Context context) {        super(context);    }    public GridViewInScrollView(Context context, AttributeSet attrs,            int defStyle) {        super(context, attrs, defStyle);    }    @Override    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,                MeasureSpec.AT_MOST);        super.onMeasure(widthMeasureSpec, expandSpec);    }}


0 0
原创粉丝点击