SettingBar - Android自定义控件

来源:互联网 发布:数据保护包括 编辑:程序博客网 时间:2024/06/12 18:36

1. 控件分析

经常在“我的”界面或者“设置”界面遇到如下的菜单效果:

效果图

如果是新手小白的话,对于这种界面可能直接就在布局里下手了,但肯定是效率不高的写法。

其实,这种效果用ListView就可以搞定了,无非就是如果此类界面较多,我们写的adapter就会多了。

自己呢,为了学习自定义View控件,于是便想把它作为一个入门,暂且称之它为SettingBar。



首先我们拿出单独的一条来做分析,并让它具有泛化性:

要点元素:

  • 顶部标题文字(默认隐藏)
  • 左侧icon(默认无,30dp*30dp)
  • 左侧文字
  • 右侧icon(默认箭头,16dp*16dp)
  • 右侧ImageView(默认无,48dp*48dp)
  • 右侧文字
  • 底部分割线(与左侧文字左边界对齐,默认显示)

其他扩展:

  • 点击事件
  • 所有文字均可设置颜色、字号
  • 所有图片可设置圆形、圆角、大小、图片资源src



通过分析顺便可做出单条完整的布局,效果如下:

完整的单条



2. 自定义属性

我们在value目录下新建一个attr.xml来编写我们的自定义控件的属性:

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="SettingBar">        <attr name="topTitle" format="string"></attr>        <attr name="topTitleSize" format="dimension"></attr>        <attr name="topTitleColor" format="color"></attr>        <attr name="topTitleBackgroundColor" format="color"></attr>        <attr name="topTitleVisibility" format="boolean"></attr>        <attr name="leftIcon" format="reference"></attr>        <attr name="leftIconVisibility" format="boolean"></attr>        <attr name="leftText" format="string"></attr>        <attr name="leftTextSize" format="dimension"></attr>        <attr name="leftTextColor" format="color"></attr>        <attr name="rightText" format="string"></attr>        <attr name="rightTextSize" format="dimension"></attr>        <attr name="rightTextColor" format="color"></attr>        <attr name="rightImage" format="reference"></attr>        <attr name="rightImageVisibility" format="boolean"></attr>        <attr name="rightIcon" format="reference"></attr>        <attr name="rightIconVisibility" format="boolean"></attr>        <attr name="bottomDividerVisibility" format="boolean"/>        <attr name="bottomDividerColor" format="color"/>        <attr name="bottomDividerHeight" format="dimension"/>        <attr name="tag" format="string"/>    </declare-styleable></resources>

看到最后有一个tag属性,这是为了方便以后在大量的控件中找到特定的控件。



3. 控件实现

由于这是一个简单的组合控件,并不需要特殊的绘制和计算,我们直接继承LinearLayout来实现。

改写构造方法:

public class SettingBar extends LinearLayout implements View.OnClickListener {    ......    public SettingBar(Context context) {         this(context, null);     }     public SettingBar(Context context, AttributeSet attrs) {         this(context, attrs, 0);     }     public SettingBar(Context context, AttributeSet attrs, int defStyleAttr) {         super(context, attrs, defStyleAttr);         initViews(context);         obtainStyledAttrs(attrs);     }    ......}

在构造方法中,我们要做的一是引入之前写好的布局和子控件,

private void initViews(Context context) {    LayoutInflater.from(context).inflate(R.layout.layout_setting_bar, this);    this.topTitleView = (TextView) findViewById(R.id.top_title);    this.leftTextView = (TextView) findViewById(R.id.left_text);    this.rightTextView = (TextView) findViewById(R.id.right_text);    this.leftIconView = (ImageView) findViewById(R.id.left_icon);    this.rightImageView = (ImageView) findViewById(R.id.right_image);    this.rightIconView = (ImageView) findViewById(R.id.right_icon);    this.bottomDividerView = findViewById(R.id.bottom_divider);    this.bodyLayout = (LinearLayout) findViewById(R.id.body_layout);    this.bodyLayout.setOnClickListener(this);}

二是获取来自xml的自定义属性,并设置默认值和来自xml的值。

/** * 获取自定义属性 * @param attrs */private void obtainStyledAttrs(AttributeSet attrs) {    TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SettingBar);    mTag = ta.getString(R.styleable.SettingBar_tag);    mTopTitle = ta.getString(R.styleable.SettingBar_topTitle);    mLeftText = ta.getString(R.styleable.SettingBar_leftText);    mRightText = ta.getString(R.styleable.SettingBar_rightText);    mTopTitleSize = (int) ta.getDimension(R.styleable.SettingBar_topTitleSize, DEFAULT_TOP_TITLE_SIZE);    mLeftTextSize = (int) ta.getDimension(R.styleable.SettingBar_leftTextSize, DEFAULT_LEFT_TEXT_SIZE);    mRightTextSize = (int) ta.getDimension(R.styleable.SettingBar_rightTextSize, DEFAULT_RIGHT_TEXT_SIZE);    mTopTitleVisible = ta.getBoolean(R.styleable.SettingBar_topTitleVisibility, false);    mBottomDividerVisible = ta.getBoolean(R.styleable.SettingBar_bottomDividerVisibility, true);    // 设置获取到的属性    setTag(mTag == null ? "" : mTag);    setTopTitle(mTopTitle == null ? "" : mTopTitle);    setLeftText(mLeftText == null ? "" : mLeftText);    setRightText(mRightText == null ? "" : mRightText);    setTopTitleSize(mTopTitleSize);    setLeftTextSize(mLeftTextSize);    setRightTextSize(mRightTextSize);    setTopTitleVisibility(mTopTitleVisible);    setBottomDividerVisibility(mBottomDividerVisible);    ta.recycle();}

最后不要忘记调用recycle()释放实例。

此外我们还要留一个接口,对外处理一些点击操作:

public interface OnBarClickListener {    void onBarClick();}private OnBarClickListener mListener;public void setOnBarClickListener(OnBarClickListener listener) {    this.mListener = listener;}@Overridepublic void onClick(View view) {    if (mListener != null) {        mListener.onBarClick();    }}

其他各种get/set方法请见GitHub上的项目源码:https://github.com/Yiiip/SettingBar。



4. 添加Model实体类

在Activity中使用的时候,除了在xml中就直接设置属性值,在java中动态设置值也是必不可少的。

除了我们在SettingBar中暴露的一些getXXX和setXXX方法,还可以加入实体类来辅助它们,配合使用,其内部属性可按需添加:

public class SettingBarModel {    private String topTitle;    private String leftText;    private String rightText;    private String rightTextColorString;    private int rightTextColorRes;    private int leftIconRes;    private int rightIconRes;    private int rightImageRes;    //... 构造方法    //... get,set方法}

有了实体类,我们在Activity中使用它就又多了一种赋值的手段。是不是多少有点在写adapter时似曾相识的感觉,不过我们不那样设计,有get和set方法就足够了。



5. 使用案例和实际效果

实际效果

MainActivity.java

public class MainActivity extends AppCompatActivity {    private LinearLayout container;    private List<SettingBar> views;    private List<SettingBarModel> models;    private int[] leftIcons = {R.drawable.ic_qq, R.drawable.ic_wechat, R.drawable.ic_weibo};    private String[] leftTexts = {"QQ", "微信", "微博"};    private String[] rightTextColors = {"#995EAADE", "#992DC100", "#99E6162D"};    private String[] titles = {"方法一:set方法", "方法二:model实体类", "方法三:xml属性"};    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        container = (LinearLayout) findViewById(R.id.container);        views = new ArrayList<>();        for (int i = 0; i < container.getChildCount(); i++) {            views.add((SettingBar) container.getChildAt(i));        }        models = new ArrayList<>();        for (int i = 0; i < leftTexts.length; i++) {            models.add(new SettingBarModel(leftTexts[i], leftIcons[i], "未绑定", rightTextColors[i]));        }        for (int i = 0; i < views.size(); i++) {            final SettingBar view = views.get(i);            if (i%3 == 0) {                view.setTopTitle(titles[i/3]);            }            if (i%3 == 2) {                view.setBottomDividerHeight(0);                // 或view.setBottomDividerVisibility(false);            }            if (i == 0) {                view.setLeftIconVisibility(false);                view.setLeftText("头像");                view.setRightImage(R.drawable.ic_icon_twitter);            }            if (i == 1) {                view.setLeftText("用户名");                view.setRightText("LYP");                view.setRightIconVisibility(false);            }            if (i == 2) {                view.setLeftText("用户等级");                view.setLeftTextColorString("#F8B250");                view.setRightIcon(R.drawable.ic_level);                view.setRightText("VIP10");            }            if (i/3 == 1) {                view.setupModel(models.get(i%3));            }            if (view.getTag() != null && view.getTag().equals("LAST")) {                view.setRightText("LAST");            }            final int index = i;            view.setOnBarClickListener(new SettingBar.OnBarClickListener() {                @Override                public void onBarClick() {                    Toast.makeText(MainActivity.this, "点击了 "+ view.getLeftText(), Toast.LENGTH_SHORT).show();                }            });        }    }}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:background="#F5F5F5"    tools:context="com.lyp.lypcustomview.MainActivity">    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:gravity="center"        android:padding="46dp"        android:background="#B0B0B0"        android:text="DEMO for SettingBar"        android:textSize="20sp"        android:textColor="#FFF"/>    <ScrollView        android:layout_width="match_parent"        android:layout_height="match_parent">        <LinearLayout            android:id="@+id/container"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:orientation="vertical">            <com.lyp.settingbar.SettingBar                android:layout_width="match_parent"                android:layout_height="wrap_content"/>            <com.lyp.settingbar.SettingBar                android:layout_width="match_parent"                android:layout_height="wrap_content"/>            <com.lyp.settingbar.SettingBar                android:layout_width="match_parent"                android:layout_height="wrap_content"/>            <com.lyp.settingbar.SettingBar                android:layout_width="match_parent"                android:layout_height="wrap_content"/>            <com.lyp.settingbar.SettingBar                android:layout_width="match_parent"                android:layout_height="wrap_content"/>            <com.lyp.settingbar.SettingBar                android:layout_width="match_parent"                android:layout_height="wrap_content"/>            <com.lyp.settingbar.SettingBar                app:leftText="通过xml属性设置"                android:layout_width="match_parent"                android:layout_height="wrap_content"                app:leftIcon="@drawable/ic_twitter"/>            <com.lyp.settingbar.SettingBar                android:layout_width="match_parent"                android:layout_height="wrap_content"                app:leftText="tag属性"                app:tag="LAST"/>        </LinearLayout>    </ScrollView></LinearLayout>



目前还存在一些不足,但是自定义控件还是要学会的。也请多多指点。
项目源码:https://github.com/Yiiip/SettingBar

注:转载请遵循CC-BY-NC-ND协议。

1 0