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协议。
- SettingBar - Android自定义控件
- [Android自定义控件] Android自定义控件
- Android自定义控件] Android自定义控件
- [Android自定义控件] Android自定义控件
- [Android自定义控件] Android自定义控件
- [Android自定义控件] Android自定义控件
- [Android自定义控件] Android自定义控件
- [Android自定义控件] Android自定义控件
- Android 自定义控件 单页翻书控件
- android虚线控件---自定义控件
- android自定义控件实例 --控件
- Android自定义控件--组合控件
- android 控件 自定义组合控件
- 【android自定义控件】ProgressBar自定义
- Android自定义控件 自定义属性
- Android自定义控件 -- 自定义View
- Android自定义控件 -- 自定义ViewGroup
- Android 自定义控件 自定义标题栏
- NSIS安装包制做软件的常用小技巧
- hello
- Unsupported major.minor version 51.0
- CSU-1409
- TODO:当PHP遇上IIS
- SettingBar - Android自定义控件
- CSU-1410
- Spring SpringMVC 整合问题
- 13.6.2
- MSSQL 索引
- 1054. 求平均值 (20)
- CSU-1505
- java中判断一个字符串是否“都为数字”和“是否包含数字”和“截取数字”
- 最小生成树模板——Jungle Roads,Constructing Roads为例