Android自定义控件

前言

Android 项目中避免不了会使用自定义控件,主要能够避免代码的冗余,使用起来也很灵活,而且也方便后期移植入其他项目。自定义控件也是面试中经常问到的东西,写过挺多自定义控件但是一直没有系统的总结过,今天再重新系统的学习一下。

自定义控件主要分为三类,自定义组合控件,自定义绘制控件,自定义继承控件。

1.自定义组合控件是项目中经常会用到,像标题栏,复用率较高的布局。

2.自定义绘制控件是面试中经常会问到的,如果需求复杂,绘制的流程也会难度加大(后期会详细介绍自定义控件的绘制)

3.自定义继承控件主要是针对Android 原生的控件进行扩展,像自定义listView 。

下面我们来详细说一下这三种自定义控件

一.自定义组合控件

组合控件的意思就是,我们并不需要自己去绘制视图上显示的内容,而只是用系统原生的控件就好了,但我们可以将几个系统原生的控件组合到一起,这样创建出的控件就被称为组合控件。

场景:一般一个项目中所有页面的标题栏大体都差不多,所以我们没有必要去每个页面都重写写布局,这个时候我们就可以用自定义组合控件

1.第一步:新建一个title.xml布局文件,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/lay"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="@color/white">

  <!--返回键-->
 <ImageView
 android:id="@+id/title_icon"
 android:layout_width="wrap_content"
 android:layout_height="30dp"
 android:layout_centerVertical="true"
 android:paddingLeft="15dp"
 android:src="@mipmap/black_back" />

 <!--关闭按钮-->
 <TextView
 android:id="@+id/tv_close"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centerVertical="true"
 android:paddingLeft="15dp"
 android:text="关闭"
 android:textColor="#333333"
 android:textSize="15sp"
 android:visibility="gone" />
 <!--标题-->
 <TextView
 android:id="@+id/title_name"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centerHorizontal="true"
 android:layout_marginBottom="13dp"
 android:layout_marginTop="13dp"
 android:ellipsize="end"
 android:maxLength="13"
 android:singleLine="true"
 android:textColor="#010101"
 android:textSize="17sp" />

 <!--右侧提示字和图标-->
 <LinearLayout
 android:id="@+id/ll_right"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignParentRight="true"
 android:layout_centerVertical="true"
 android:gravity="center_vertical"
 android:orientation="horizontal">
  <!--图标-->
 <ImageView
 android:id="@+id/iv_right"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginRight="3dp" />
 <!--提示文字-->
 <TextView
 android:id="@+id/tv_right"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginRight="15dp"
 android:textColor="#333333"
 android:textSize="15sp" />     
 </LinearLayout>
<View
 android:layout_width="match_parent"
 android:layout_height="1dp"
 android:layout_below="@+id/title_name"
 android:background="@color/line_d7" />
</RelativeLayout>

UI效果如图

image.png

2.第二步:接下来我们来创建一个View 集成RelativeLayout 如

public class WhitePublicTitleView extends RelativeLayout {
    View mView;
    ImageView title_icon;
    TextView title_name;
    RelativeLayout lay;
    ImageView iv_right;
    TextView tv_right;
    LinearLayout ll_right;
    TextView tv_close;

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

    public WhitePublicTitleView(Context context) {
        super(context);
    }

    @Override
 protected void onFinishInflate() {
        super.onFinishInflate();
        mView = LayoutInflater.from(getContext()).inflate(R.layout.public_title_view_white, this);
        initView();
    }

    public void setTitleBg() {
        lay.setBackgroundColor(Color.parseColor("#de3031"));
    }

    public void initView() {
        title_icon = (ImageView) mView.findViewById(R.id.title_icon);
        title_name = (TextView) mView.findViewById(R.id.title_name);
        lay = (RelativeLayout) mView.findViewById(R.id.lay);
        iv_right = (ImageView) mView.findViewById(R.id.iv_right);
        tv_right = (TextView) mView.findViewById(R.id.tv_right);
        tv_close = (TextView) mView.findViewById(R.id.tv_close);

        ll_right = (LinearLayout) mView.findViewById(R.id.ll_right);

    }

    public void setBackListener(OnClickListener clickListener) {
        title_icon.setOnClickListener(clickListener);
        tv_close.setOnClickListener(clickListener);
    }

    public void setBackState(int state) {
        title_icon.setVisibility(state);
    }

    public void setTitleNam(String name) {
        title_name.setText(name);
    }

    public void setRight(String state, int icon) {
        tv_right.setText(state);
        iv_right.setImageResource(icon);
    }

    public void setRightListener(OnClickListener clickListener) {
        ll_right.setOnClickListener(clickListener);
    }

    //展示文字关闭按钮
 public void showTextClose() {
        tv_close.setVisibility(View.VISIBLE);
        title_icon.setVisibility(View.GONE);
    }
}

3.第三步:我们使用自定义组合控件

布局文件引入

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@color/f2"
 android:divider="@drawable/divider"
 android:orientation="vertical">

    <!--标题-->
 <com.jyjt.ydyl.Widget.WhitePublicTitleView
 android:id="@+id/title_collect"
 android:layout_width="match_parent"
 android:layout_height="wrap_content" />

</LinearLayout>
代码初始化具体数据
 
title_collect= (ImageView) mView.findViewById(R.id.title_collect);
title_collect.setTitleNam("我的收藏");
title_collect.setBackListener(new View.OnClickListener() {
    @Override
 public void onClick(View v) {
        SwitchActivityManager.exitActivity(MyCollectActivity.this);
    }
});

总结:自定义控件在创建和使用起来都很简单。

二.自定义绘制控件

自定义绘制控件的意思就是,这个View上所展现的内容全部都是我们自己绘制出来的。绘制的代码是写在onDraw()方法中的

场景:自定义一个计数器View,这个View可以响应用户的点击事件,并自动记录一共点击了多少次。新建一个CounterView继承自View,代码如下所示:

1.第一步:创建一个类集成View 进行绘制

public class CounterView extends View implements OnClickListener {

    private Paint mPaint;

    private Rect mBounds;

    private int mCount;

    public CounterView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBounds = new Rect();
        setOnClickListener(this);
    }

    @Override
 protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(Color.BLUE);
        canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
        mPaint.setColor(Color.YELLOW);
        mPaint.setTextSize(30);
        String text = String.valueOf(mCount);
        mPaint.getTextBounds(text, 0, text.length(), mBounds);
        float textWidth = mBounds.width();
        float textHeight = mBounds.height();
        canvas.drawText(text, getWidth() / 2 - textWidth / 2, getHeight() / 2+ textHeight / 2, mPaint);
    }

    @Override
 public void onClick(View v) {
        mCount++;
        invalidate();
    }

}  

可以看到,首先我们在onClick()方法调用调用invalidate()方法会导致视图进行重绘,因此onDraw()方法在稍后就将会得到调用。

主要的逻辑当然就是写在onDraw()方法里首先是将Paint画笔设置为蓝色,然后调用Canvas的drawRect()方法绘制了一个矩形背景,接着将画笔设置为黄色,绘制当前的计数,注意这里先是调用了getTextBounds()方法来获取到文字的宽度和高度,然后调用了drawText()方法去进行绘制。

2.第二步:使用自定义绘制控件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<com.example.customview.CounterView
 android:layout_width="100dp"
 android:layout_height="100dp"
 android:layout_centerInParent="true" />

</RelativeLayout>  

效果如图:


image.png

可以看到,这里我们将CounterView放入了一个RelativeLayout中,然后可以像使用普通控件来给CounterView指定各种属性,比如通过layout_width和layout_height来指定CounterView的宽高,通过android:layout_centerInParent来指定它在布局里居中显示。只不过需要注意,自定义的View在使用的时候一定要写出完整的包名,不然系统将无法找到这个View。

三.自定义继承控件

继承控件,我们并不需要自己重头去实现一个控件,只需要去继承一个现有的控件,然后在这个控件上增加一些新的功能,就可以形成一个自定义的控件了。这种自定义控件的特点就是不仅能够按照我们的需求加入相应的功能,还可以保留原生控件的所有功能。

场景:我们做一个简单的自定义一个diaolog ,点击确定按钮消失,(主要集成具体的原生控件)

第一步:创建一个布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/confirm_layout"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:background="@drawable/background_corners_white"
 android:orientation="vertical">

    <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_gravity="center_horizontal"
 android:layout_margin="5dip"
 android:background="@color/white"
 android:gravity="center"
 android:orientation="vertical">

        <ImageView

 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginBottom="18dp"
 android:layout_marginTop="24dp"
 android:src="@mipmap/ic_build_dialog" />
    </LinearLayout>

    <View
 android:layout_width="match_parent"
 android:layout_height="0.5dip"
 android:background="@color/line" />

 

 <Button
 android:id="@+id/ok_btn"
 android:layout_width="0dp"
 android:layout_height="match_parent"
 android:layout_weight="1"
 android:background="@drawable/commenttext"
 android:gravity="center"
 android:text="知道了"
 android:textColor="@color/de30"
 android:textSize="16sp" />
  

</LinearLayout>

第二部:创建一个类继承Dialog

public class BuildingDialog extends Dialog {
    public View mView;
    LayoutInflater inflater;
    Button ok_btn;
    public Context mContext;
    DialogCallBack mDialogCallBack;

    public BuildingDialog(Context context) {
        this(context, R.style.dialog, null);
        this.mContext = context;
    }

    public BuildingDialog(Context context, int themeResId, String content) {
        super(context, themeResId);
        this.mContext = context;
    }

    @Override
 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        inflater = LayoutInflater.from(mContext);
        mView = inflater.inflate(R.layout.dialog_build, null);
        setContentView(mView);
        initView();
        ok_btn.setOnClickListener(new View.OnClickListener() {
            @Override
 public void onClick(View v) {
                dismiss();
      // mDialogCallBack.clickokBtn();
 }
        });
    }

    public void initView() {
        ok_btn = (Button) mView.findViewById(R.id.ok_btn);
    }

    public void setDialogCallBack(DialogCallBack dialogCallBack) {
        this.mDialogCallBack = dialogCallBack;
    }

    public interface DialogCallBack {
        void clickokBtn();
    }
}

第三步:使用自定义继承控件

BuildingDialog mBuildingDialog= new BuildingDialog(mContext);
mBuildingDialog.show(); //展示
mBuildingDialog.setDialogCallBack(this); //回调监听

四.自定义属性

使用自定义控件的时候有时会创建自己的控件的属性,下面我们来讲讲自定义属性怎么用

主要步骤

1.定义declare-styleable,添加attr

2.使用TypedArray获取自定义属性

3.设置到View上

第一步:定义declare-styleable,添加attr

第二步:稍后更新

第三步:稍后更新

总结:

虽然每个例子都很简单,但是万变不离其宗,复杂的View也是由这些简单的原理堆积出来的。后期会写深入了解View系列的文章,感谢大家有耐心看到最后。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,395评论 25 707
  • 相关文章 Android View体系(一)视图坐标系Android View体系(二)实现View滑动的六种方法...
    刘望舒阅读 1,185评论 0 19
  • 一、自定义组合控件介绍 开发中,为了使用的方便,经常把一些控件组合成一个控件,那样就成为了我们的自定义组合控件,严...
    小楠总阅读 832评论 0 4
  • 感恩金刚智慧种子法则,让重庆的煌华的项目转折,种子开花,期待结出果实。感恩同事的直言相告,让我有了更好的成长,感恩...
    日精进_a07d阅读 60评论 0 2
  • “戴着这个就感觉不到时间了” bg: 注意力辅助器最开始是为了长时间的太空航行设计的。 旧时代科幻片中常见的休眠技...
    现代魔法书阅读 208评论 0 0