SettingBar - Android自定义控件

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;
}

@Override
public 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协议。

猜你喜欢

转载自blog.csdn.net/skipperkevin/article/details/54974167