如何更好的通过Inflate layout的方式来实现自定义view

本篇文章讲的是如何用现有控件产生一个组合控件的方法,十分简单实用。现在开始!

一、需求

我们要实现一个有红点和文字的按钮控件,就像下面这样:

二、实现

我的思路是让一个button和一个textview进行组合。

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    >

    <RadioButton
        android:id="@+id/tab_btn"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"
        android:button="@null"
        android:drawablePadding="1dp"
        android:gravity="center"
        android:textSize="11sp"
        />

    <TextView
        android:id="@+id/tab_hint"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:paddingLeft="4dp"
        android:paddingRight="4dp"
        android:layout_toRightOf="@+id/tab_btn"
        android:layout_marginLeft="-5dp"
        android:textSize="11sp"
        android:minHeight="6dp"
        android:singleLine="true"

        />

</merge>

可以看到最外层我用了merge标签,这是因为我需要把这个xml加载到一个自定义的RelativeLayout中。merge标签主要是用来避免重复嵌套的。

接着我在java代码中加载这个xml文件

public class BottomTab extends RelativeLayout implements BottomTabImpl {

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

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

    public BottomTab(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initViews();
    }private void initViews() {
        inflate(getContext(), R.layout.test_xml, this);

这样就完成了一个初步的自定义view,但我们要知道merge标签是有弊端的。<merge>标签可以融合其内容,但是不包括自身,因此顶层的属性都丢失了。而且用了merge,在布局中因为不知道最外层是什么控件,所以就不能很好的进行预览。预览的问题无法解决,但是我们有方法让控件最外层的属性加回来。

三、解决merge属性丢失的问题

有三种办法可以将它们添加回来:

1)在代码中添加

private void initViews() {
        inflate(getContext(), R.layout.card, this);
        // add bg to root view
        setBackgroundColor(getResources().getColor(R.color.card_background));

        //Add missing top level attributes
        int padding = (int)getResources().getDimension(R.dimen.card_padding);
        setPadding(padding, padding, padding, padding);

       ……
    }

2)在控件被使用的时候添加丢失的属性

<FrameLayout 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">

    <com.trickyandroid.customview.app.view.Card
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"


        android:background="@color/card_background"
        android:padding="@dimen/card_padding"

/>

</FrameLayout>

3)定义一个stylable 属性将这些值通过style提供给控件

attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="Card">
        <attr name="cardStyle" format="reference"/>
    </declare-styleable>
</resources>

style.xml

  <!-- Base application theme. -->
    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
        <item name="android:windowBackground">@color/main_background</item>
        <item name="cardStyle">@style/CardStyle</item>
    </style>

    <style name="CardStyle" parent="android:Widget.Holo.Light">
        <item name="android:padding">@dimen/card_padding</item>
        <item name="android:background">@color/card_background</item>
    </style>

</resources>

Card.java

public class Card extends RelativeLayout {

    public Card(Context context) {
        super(context, null, R.attr.cardStyle);
        init();
    }

    public Card(Context context, AttributeSet attrs) {
        super(context, attrs, R.attr.cardStyle);
        init();
    }
    .....

需要注意的是要在view的构造方法中要传入R.attr.xxx的文件,让控件去调用。为了更加说明这点,我举个toolbar的例子来说明。

首先,toolbar在系统中定义了这样一个attr:

<declare-styleable name="Toolbar">
        <attr name="titleTextAppearance" format="reference" />
        <attr name="subtitleTextAppearance" format="reference" />
        <attr name="title" />
        <attr name="subtitle" />
        <attr name="android:gravity" />
        <attr name="titleMargins" format="dimension" />
        <attr name="titleMarginStart" format="dimension" />
        <attr name="titleMarginEnd" format="dimension" />
        <attr name="titleMarginTop" format="dimension" />
        <attr name="titleMarginBottom" format="dimension" />
        <attr name="contentInsetStart" />
        <attr name="contentInsetEnd" />
        <attr name="contentInsetLeft" />
        <attr name="contentInsetRight" />
        <attr name="maxButtonHeight" format="dimension" />
        <attr name="collapseIcon" format="reference" />
        <attr name="collapseContentDescription" format="string" />
        <attr name="popupTheme" />
        <attr name="navigationIcon" format="reference" />
        <attr name="navigationContentDescription" format="string" />
        <attr name="android:minHeight" />
    </declare-styleable>

然后在代码中进行了如下的设置:

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

    public Toolbar(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, R.attr.toolbarStyle);
    }

    public Toolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        // Need to use getContext() here so that we use the themed context
        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), attrs,
                R.styleable.Toolbar, defStyleAttr, 0);

        mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0);
        mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0);
        mGravity = a.getInteger(R.styleable.Toolbar_android_gravity, mGravity);
        mButtonGravity = Gravity.TOP;
        mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom =
                a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0);

这样我们就知道这个view用到了R.attr.toolbarStyle的属性,所以如果我们想要设置一个全局的属性,那么可以在theme中进行设置即可。

 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->

        <item name="toolbarStyle">@style/ToolbarStyle</item>
        <!--<item name="R.attr.actionOverflowMenuStyle" />-->

    </style>

设置具体的值:

<style name="ToolbarStyle" parent="Base.Widget.AppCompat.Toolbar">
        <item name="titleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Title</item>
        <item name="subtitleTextAppearance">@style/TextAppearance.Widget.AppCompat.Toolbar.Subtitle</item>
        <item name="android:minHeight">?attr/actionBarSize</item>
        <item name="titleMargins">0dp</item>
        <item name="maxButtonHeight">56dp</item>
        <item name="collapseIcon">?attr/homeAsUpIndicator</item>
        <item name="collapseContentDescription">@string/abc_toolbar_collapse_description</item>
        <item name="contentInsetStart">0dp</item>
        <item name="android:minWidth">20dp</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">?attr/actionBarSize</item>
    </style>

参考自:http://www.devtf.cn/?p=422

时间: 2024-11-09 21:07:55

如何更好的通过Inflate layout的方式来实现自定义view的相关文章

UI--从学习styleable自定义view属性到一点儿更有意思的尝试

<代码里的世界> -UI篇 用文字札记描绘自己 android学习之路 转载请保留出处 by Qiao http://blog.csdn.net/qiaoidea/article/details/45599593 [导航] - 多行文本折叠展开 自定义布局View实现多行文本折叠和展开 1.概述 前面封装view的时候用到了自定义属性,觉得有必要单独讲一下这部分,但是呢,又不想向其他文章一样千篇一律地写这些东西.所以呢,后便会加一些临时的发散思维,引用点有意思的东西.分享东西嘛,随性点儿. 回

继承于Layout的自定义View减少布局层次

不管是为了封装也好,实现特殊的效果也好,大家或多或少都会进行自定义View的实践,这中间又主要有两种:一种是继承于View或ViewGroup,还有一个是继承于各种已存在的Layout使用XML来写. 今天要来讨论的是第二种,实践就不详细说了,这里主要是针对这种方式带来的布局层次过深的问题提出两个方案. 第一种,注意在布局xml中使用merge,千万不要误解这个只在FrameLayout时候才能用哦,这个的准确作用是在解析XML布局时由此标志位就不解析直接将这一层忽略,将下面层次的view直接添

Enhancing Android UI with Custom Views 通过自定义view来让你的UI更屌!

Enhancing Android UI with Custom Views (通过自定义view来让你的UI更屌) Table of Contents: Custom View View Measurement View Drawing Custom Attributes Custom ViewGroup ViewGroup Measurement Layout ViewGroup Drawing More Custom Attributes There are many great adva

通过自定义view替代layout.xml表示activity的界面

创建了一个新项目之后, 在src内新建一个类public class 类名 extends View 1 package com.example.activity_view; 2 3 import android.content.Context; 4 import android.graphics.Canvas; 5 import android.graphics.Color; 6 import android.graphics.Paint; 7 import android.view.View

改写控件之《自定义View,让你整个Layout像横向温度计一般》

转载请注明出处:王亟亟的大牛之路 恐惧自我受苦的人,已经正因自我的恐惧在受苦. 我们平时的进度条一般是下面这样子的 今天上的一个效果是这样的(初始化) 动起来后是这样的 给与用户一种新的体验吧,贴下项目结构 自定义控件:ProgressLayout 配套的监听事件:ProgressLayoutListener 适配器:RecylerListAdapter 对象类:Track(实际生产应该是 Json对象之类的) OK,废话不多说,上代码 public class Track { private

一种更高查询性能的列存储方式MaxMinT 第一部分

简介本文描述了一种列存储方式和对应的查询方法,这种存储方式具有更好的查询性能和更小的存储空间. And查询 本文先用直观的图形方式展示and查询时的方式,这也是算法要解决的问题核心.通常在OLAP数据查询时,需要进行and处理,例如你需要获取 year = 2017 and customer = 13 的数据,这在列存储中实际是对值 year的2017这个列和 customer的13列进行and操作,而这些列一般都使用位图的方式存储.市面上有很多位图的存储方式,比如WAH, EWAH, Conc

各个浏览器开启CSS Grid Layout的方式

2017年3月,Chrome.Firefox将开启默认支持. 当然对于很多人等不及浏览器默认支持,想提前体验一把,这里提供一些打开方式: 1.Chrome 在浏览器中输入:chrome://flags/#enable-experimental-web-platform-features 然后,对第一个实验性网络平台功能,点击启用. 然后重启浏览器,然后便能体验到了. 2.Opera 在浏览器中输入:opera://flags/#enable-experimental-web-platform-f

layout布局方式的使用建立

这个布局总体感觉还是蛮不错的,虽然在某种程度上让我们失去了一定的代码操控能力,但是,他做的还是很不错的,至少我们用JavaScript的纯原生代码实现起来较为复杂,这样的布局方式同样的遵循了div+CSS的布局方式,不得不说的是,有一些限制css,在操作样式的时候会出现一些小的错误,但也是微不足道的,毕竟只是前台代码,不涉及太多的安全性问题.下面是layout的初步建立方式: html代码: <!DOCTYPE html> <html> <head> <title

极光一键登录:更快捷、安全的登录认证方式,简单集成即可实现

作为一项必不可少的操作,现有登录方式的门槛并不算低,甚至存在一些难以调和的矛盾:愈发健忘的我们需要记住所有账号和密码,愈发追求效率的我们需要等待并输入短信验证码方能完成登录,愈发重视安全和隐私的我们需要面对明文验证.短信劫持和信息泄露的风险. 是否有一种登录方式在保证安全的同时,还能提升我们的使用效率?极光认证正是一款为此而生的产品. 三网认证.无需密码:降低用户登录门槛,提升转化率 注册和登录是用户与产品接触的第一步,一个更省时.直观的登录体验可以显著降低产品的使用门槛,提升用户转化率.与传统