首先看效果图,

看下这两个界面,第一个中用到了一个自定义的FlowRadioGroup,支持复合子控件,自定义布局;

第二个界面中看到了输入的数字 自动4位分割了吧;也用到了自定义的DivisionEditText控件。

下面直接看源码FlowRadioGroup了;

 /*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package com.newgame.sdk.view; import java.util.ArrayList; import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.RadioButton; /** 可以放多种布局控件,能找到radiobutton */
public class FlowRadioGroup extends LinearLayout {
// holds the checked id; the selection is empty by default
private int mCheckedId = -1;
// tracks children radio buttons checked state
private CompoundButton.OnCheckedChangeListener mChildOnCheckedChangeListener;
// when true, mOnCheckedChangeListener discards events
private boolean mProtectFromCheckedChange = false;
private OnCheckedChangeListener mOnCheckedChangeListener;
private PassThroughHierarchyChangeListener mPassThroughListener; // 存放当前的radioButton
private ArrayList<RadioButton> radioButtons; public FlowRadioGroup(Context context) {
super(context);
setOrientation(VERTICAL);
init();
} public FlowRadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} private void init() {
mChildOnCheckedChangeListener = new CheckedStateTracker();
mPassThroughListener = new PassThroughHierarchyChangeListener();
super.setOnHierarchyChangeListener(mPassThroughListener);
radioButtons = new ArrayList<RadioButton>();
} @Override
public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
// the user listener is delegated to our pass-through listener
mPassThroughListener.mOnHierarchyChangeListener = listener;
} @Override
protected void onFinishInflate() {
super.onFinishInflate(); // checks the appropriate radio button as requested in the XML file
if (mCheckedId != -1) {
mProtectFromCheckedChange = true;
setCheckedStateForView(mCheckedId, true);
mProtectFromCheckedChange = false;
setCheckedId(mCheckedId);
}
} @Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (child instanceof RadioButton) {
final RadioButton button = (RadioButton) child;
radioButtons.add(button); if (button.isChecked()) {
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
setCheckedId(button.getId());
}
} else if (child instanceof ViewGroup) {// 如果是复合控件
// 遍历复合控件
ViewGroup vg = ((ViewGroup) child);
setCheckedView(vg);
} super.addView(child, index, params);
} /** 查找复合控件并设置radiobutton */
private void setCheckedView(ViewGroup vg) {
int len = vg.getChildCount();
for (int i = 0; i < len; i++) {
if (vg.getChildAt(i) instanceof RadioButton) {// 如果找到了,就设置check状态
final RadioButton button = (RadioButton) vg.getChildAt(i);
// 添加到容器
radioButtons.add(button);
if (button.isChecked()) {
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
setCheckedId(button.getId());
}
} else if (vg.getChildAt(i) instanceof ViewGroup) {// 迭代查找并设置
ViewGroup childVg = (ViewGroup) vg.getChildAt(i);
setCheckedView(childVg);
}
}
} /** 查找复合控件并设置id */
private void setCheckedId(ViewGroup vg) {
int len = vg.getChildCount();
for (int i = 0; i < len; i++) {
if (vg.getChildAt(i) instanceof RadioButton) {// 如果找到了,就设置check状态
final RadioButton button = (RadioButton) vg.getChildAt(i);
int id = button.getId();
// generates an id if it's missing
if (id == View.NO_ID) {
id = button.hashCode();
button.setId(id);
}
button.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
} else if (vg.getChildAt(i) instanceof ViewGroup) {// 迭代查找并设置
ViewGroup childVg = (ViewGroup) vg.getChildAt(i);
setCheckedId(childVg);
}
}
} /** 查找radioButton控件 */
public RadioButton findRadioButton(ViewGroup group) {
RadioButton resBtn = null;
int len = group.getChildCount();
for (int i = 0; i < len; i++) {
if (group.getChildAt(i) instanceof RadioButton) {
resBtn = (RadioButton) group.getChildAt(i);
} else if (group.getChildAt(i) instanceof ViewGroup) {
resBtn = findRadioButton((ViewGroup) group.getChildAt(i));
findRadioButton((ViewGroup) group.getChildAt(i));
break;
}
}
return resBtn;
} /** 返回当前radiobutton控件的count */
public int getRadioButtonCount() {
return radioButtons.size();
} /** 返回当前index的radio */
public RadioButton getRadioButton(int index) {
return radioButtons.get(index);
} /**
* <p>
* Sets the selection to the radio button whose identifier is passed in
* parameter. Using -1 as the selection identifier clears the selection;
* such an operation is equivalent to invoking {@link #clearCheck()}.
* </p>
*
* @param id
* the unique id of the radio button to select in this group
*
* @see #getCheckedRadioButtonId()
* @see #clearCheck()
*/
public void check(int id) {
// don't even bother
if (id != -1 && (id == mCheckedId)) {
return;
} if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
} if (id != -1) {
setCheckedStateForView(id, true);
} setCheckedId(id);
} private void setCheckedId(int id) {
mCheckedId = id;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}
} private void setCheckedStateForView(int viewId, boolean checked) {
View checkedView = findViewById(viewId);
if (checkedView != null && checkedView instanceof RadioButton) {
((RadioButton) checkedView).setChecked(checked);
}
} /**
* <p>
* Returns the identifier of the selected radio button in this group. Upon
* empty selection, the returned value is -1.
* </p>
*
* @return the unique id of the selected radio button in this group
*
* @see #check(int)
* @see #clearCheck()
*/
public int getCheckedRadioButtonId() {
return mCheckedId;
} /**
* <p>
* Clears the selection. When the selection is cleared, no radio button in
* this group is selected and {@link #getCheckedRadioButtonId()} returns
* null.
* </p>
*
* @see #check(int)
* @see #getCheckedRadioButtonId()
*/
public void clearCheck() {
check(-1);
} /**
* <p>
* Register a callback to be invoked when the checked radio button changes
* in this group.
* </p>
*
* @param listener
* the callback to call on checked state change
*/
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
mOnCheckedChangeListener = listener;
} /**
* {@inheritDoc}
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new FlowRadioGroup.LayoutParams(getContext(), attrs);
} /**
* {@inheritDoc}
*/
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof FlowRadioGroup.LayoutParams;
} @Override
protected LinearLayout.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
} /**
* <p>
* This set of layout parameters defaults the width and the height of the
* children to {@link #WRAP_CONTENT} when they are not specified in the XML
* file. Otherwise, this class ussed the value read from the XML file.
* </p>
*
* <p>
* See {@link android.R.styleable#LinearLayout_Layout LinearLayout
* Attributes} for a list of all child view attributes that this class
* supports.
* </p>
*
*/
public static class LayoutParams extends LinearLayout.LayoutParams {
/**
* {@inheritDoc}
*/
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
} /**
* {@inheritDoc}
*/
public LayoutParams(int w, int h) {
super(w, h);
} /**
* {@inheritDoc}
*/
public LayoutParams(int w, int h, float initWeight) {
super(w, h, initWeight);
} /**
* {@inheritDoc}
*/
public LayoutParams(ViewGroup.LayoutParams p) {
super(p);
} /**
* {@inheritDoc}
*/
public LayoutParams(MarginLayoutParams source) {
super(source);
} /**
* <p>
* Fixes the child's width to
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and the
* child's height to
* {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} when not
* specified in the XML file.
* </p>
*
* @param a
* the styled attributes set
* @param widthAttr
* the width attribute to fetch
* @param heightAttr
* the height attribute to fetch
*/
@Override
protected void setBaseAttributes(TypedArray a, int widthAttr,
int heightAttr) { if (a.hasValue(widthAttr)) {
width = a.getLayoutDimension(widthAttr, "layout_width");
} else {
width = WRAP_CONTENT;
} if (a.hasValue(heightAttr)) {
height = a.getLayoutDimension(heightAttr, "layout_height");
} else {
height = WRAP_CONTENT;
}
}
} /**
* <p>
* Interface definition for a callback to be invoked when the checked radio
* button changed in this group.
* </p>
*/
public interface OnCheckedChangeListener {
/**
* <p>
* Called when the checked radio button has changed. When the selection
* is cleared, checkedId is -1.
* </p>
*
* @param group
* the group in which the checked radio button has changed
* @param checkedId
* the unique identifier of the newly checked radio button
*/
public void onCheckedChanged(FlowRadioGroup group, int checkedId);
} private class CheckedStateTracker implements
CompoundButton.OnCheckedChangeListener {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
// prevents from infinite recursion
if (mProtectFromCheckedChange) {
return;
} mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false; int id = buttonView.getId();
setCheckedId(id);
}
} /**
* <p>
* A pass-through listener acts upon the events and dispatches them to
* another listener. This allows the table layout to set its own internal
* hierarchy change listener without preventing the user to setup his.
* </p>
*/
private class PassThroughHierarchyChangeListener implements
ViewGroup.OnHierarchyChangeListener {
private ViewGroup.OnHierarchyChangeListener mOnHierarchyChangeListener; public void onChildViewAdded(View parent, View child) {
if (parent == FlowRadioGroup.this && child instanceof RadioButton) {
int id = child.getId();
// generates an id if it's missing
if (id == View.NO_ID) {
id = child.hashCode();
child.setId(id);
}
((RadioButton) child)
.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
} else if (parent == FlowRadioGroup.this
&& child instanceof ViewGroup) {// 如果是复合控件
// 查找并设置id
setCheckedId((ViewGroup) child);
} if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewAdded(parent, child);
}
} public void onChildViewRemoved(View parent, View child) {
if (parent == FlowRadioGroup.this && child instanceof RadioButton) {
((RadioButton) child).setOnCheckedChangeListener(null);
} else if (parent == FlowRadioGroup.this
&& child instanceof ViewGroup) {
findRadioButton((ViewGroup) child).setOnCheckedChangeListener(
null);
}
if (mOnHierarchyChangeListener != null) {
mOnHierarchyChangeListener.onChildViewRemoved(parent, child);
}
}
}
}

简单讲解下我的实现:

1)在addview方法中,加上判断,当前子控件是否为viewgroup类型

@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if (child instanceof RadioButton) {
final RadioButton button = (RadioButton) child;
radioButtons.add(button);//将找到的控件添加到集合中 if (button.isChecked()) {
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
setCheckedId(button.getId());
}
} else if (child instanceof ViewGroup) {// 如果是复合控件
// 遍历复合控件
ViewGroup vg = ((ViewGroup) child);
setCheckedView(vg);
} super.addView(child, index, params);
} /** 查找复合控件并设置radiobutton */
private void setCheckedView(ViewGroup vg) {
int len = vg.getChildCount();
for (int i = 0; i < len; i++) {
if (vg.getChildAt(i) instanceof RadioButton) {// 如果找到了,就设置check状态
final RadioButton button = (RadioButton) vg.getChildAt(i);
// 添加到容器
radioButtons.add(button);
if (button.isChecked()) {
mProtectFromCheckedChange = true;
if (mCheckedId != -1) {
setCheckedStateForView(mCheckedId, false);
}
mProtectFromCheckedChange = false;
setCheckedId(button.getId());
}
} else if (vg.getChildAt(i) instanceof ViewGroup) {// 迭代查找并设置
ViewGroup childVg = (ViewGroup) vg.getChildAt(i);
setCheckedView(childVg);
}
}
}

2)定义一个数组存放当前所有查到到的radiobutton;

3)在onChildViewAdded方法中,判断新添加的子控件是否为viewgroup类型

else if (parent == FlowRadioGroup.this
&& child instanceof ViewGroup) {// 如果是复合控件
// 查找并设置id
setCheckedId((ViewGroup) child);
}

  

/** 查找复合控件并设置id */
private void setCheckedId(ViewGroup vg) {
int len = vg.getChildCount();
for (int i = 0; i < len; i++) {
if (vg.getChildAt(i) instanceof RadioButton) {// 如果找到了,就设置check状态
final RadioButton button = (RadioButton) vg.getChildAt(i);
int id = button.getId();
// generates an id if it's missing
if (id == View.NO_ID) {
id = button.hashCode();
button.setId(id);
}
button.setOnCheckedChangeListener(mChildOnCheckedChangeListener);
} else if (vg.getChildAt(i) instanceof ViewGroup) {// 迭代查找并设置
ViewGroup childVg = (ViewGroup) vg.getChildAt(i);
setCheckedId(childVg);
}
}
}

下面是DivisionEditText的源码;

 package com.newgame.sdk.view;

 import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.View;
import android.widget.EditText; /**
* 分割输入框
*
* @author Administrator
*
*/
public class DivisionEditText extends EditText { /* 每组的长度 */
private Integer eachLength = 4;
/* 分隔符 */
private String delimiter = " "; private String text = ""; public DivisionEditText(Context context) {
super(context);
init();
} public DivisionEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init(); } public DivisionEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
} /**
* 初始化
*/
public void init() { // 内容变化监听
this.addTextChangedListener(new DivisionTextWatcher());
// 获取焦点监听
this.setOnFocusChangeListener(new DivisionFocusChangeListener());
} /**
* 文本监听
*
* @author Administrator
*
*/
private class DivisionTextWatcher implements TextWatcher { @Override
public void afterTextChanged(Editable s) {
} @Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
} @Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// 统计个数
int len = s.length();
if (len < eachLength)// 长度小于要求的数
return;
if (count > 1) {
return;
}
// 如果包含空格,就清除
char[] chars = s.toString().replace(" ", "").toCharArray();
len = chars.length;
// 每4个分组,加上空格组合成新的字符串
StringBuffer sb = new StringBuffer();
for (int i = 0; i < len; i++) {
if (i % eachLength == 0 && i != 0)// 每次遍历到4的倍数,就添加一个空格
{
sb.append(" ");
sb.append(chars[i]);// 添加字符
} else {
sb.append(chars[i]);// 添加字符
}
}
// 设置新的字符到文本
// System.out.println("*************" + sb.toString());
text = sb.toString();
setText(text);
setSelection(text.length());
}
} /**
* 获取焦点监听
*
* @author Administrator
*
*/
private class DivisionFocusChangeListener implements OnFocusChangeListener { @Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
// 设置焦点
setSelection(getText().toString().length());
}
}
} /** 得到每组个数 */
public Integer getEachLength() {
return eachLength;
} /** 设置每组个数 */
public void setEachLength(Integer eachLength) {
this.eachLength = eachLength;
} /** 得到间隔符 */
public String getDelimiter() {
return delimiter;
} /** 设置间隔符 */
public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
} }

上面代码实现逻辑:在TextWatcher的onTextChanged方法中判断当前输入的字符,然后没4位添加一个空格,组成新的字符

@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// 统计个数
int len = s.length();
if (len < eachLength)// 长度小于要求的数
return;
if (count > 1) {// 设置新字符串的时候,直接返回
return;
}
// 如果包含空格,就清除
char[] chars = s.toString().replace(" ", "").toCharArray();
len = chars.length;
// 每4个分组,加上空格组合成新的字符串
StringBuffer sb = new StringBuffer();
for (int i = 0; i < len; i++) {
if (i % eachLength == 0 && i != 0)// 每次遍历到4的倍数,就添加一个空格
{
sb.append(" ");
sb.append(chars[i]);// 添加字符
} else {
sb.append(chars[i]);// 添加字符
}
}
// 设置新的字符到文本
// System.out.println("*************" + sb.toString());
text = sb.toString();
setText(text);
setSelection(text.length());
}

还有其他两个自定义控件也在项目中,这里界面没体现出来,我已经放在项目中了;

欢迎大家找出代码中的存在bug!!!!

最后附上代码下载地址:http://www.eoeandroid.com/forum.php?mod=attachment&aid=MTIwMDM1fDM5NTYzZjQ3fDEzOTY0Mjc4NDF8NzU4MzI1fDMyODQyNw%3D%3D

介绍几个工作开发中封装的好用的android自定义控件的更多相关文章

  1. 项目开发中封装一个BarButtonItem类别-很实用

    Encapsulates a TabBarItem--封装一个BarButtonItem类 在我们程序的导航栏的左边或右边一般都会有这样的BarButtonItem,用来界面之间的跳转 如果我们有很多 ...

  2. 新手介绍简单一下iOS开发中几种界面传值

    首先在处理iOS-UI中,也许在很多地方需要用到两种甚至多种不同界面之间的传值,相比这也是很多iOS入门成员头疼问题,同样作为新手的我在接触这类传值时候也一脸懵然,经过一段时间的研究,对于简单的传值有 ...

  3. [工作总结]jQuery在工作开发中常用代码片段集锦(1-10)

    1.jQuery,JS实现tab切换 原生JS实现 HTML代码如下: <div class="wrap"> <ul id="tag"> ...

  4. Android开发中遇到的问题(二)——新建android工程的时候eclipse没有生成MainActivity和layout布局

    一.新建android工程的时候eclipse没有生成MainActivity和layout布局 最近由于工作上的原因,开始学习Android开发,在入门的时候就遇到了不少的坑,遇到的第一个坑就是&q ...

  5. iOS-Runtime在开发中的使用及相关面试题

    OC语言中最为强大的莫过于OC的运行时机制-Runtime,但因其比较接近底层,一旦使用Runtime出现bug,将很难调试,所以Runtime在开发中能不用就不用.下面我将介绍一些Runtime在开 ...

  6. 简单讲解iOS应用开发中的MD5加密的相关使用<转>

    这篇文章主要介绍了iOS应用开发中的MD5加密的相关使用,示例代码基于传统的Objective-C,需要的朋友可以参考下 一.简单说明 1.说明 在开发应用的时候,数据的安全性至关重要,而仅仅用POS ...

  7. 简单讲解iOS应用开发中的MD5加密的相关使用

      简单讲解iOS应用开发中的MD5加密的相关使用   作者:文顶顶 字体:[增加 减小] 类型:转载 时间:2015-12-19 我要评论 这篇文章主要介绍了iOS应用开发中的MD5加密的相关使用, ...

  8. iOS开发中的Html解析方法

    iOS开发中的Html解析方法 本文作者为大家介绍了在iOS开发中的Html解析方法,并同时提供了Demo代码的下载链接,Demo 解析了某个网站(具体可在代码中查看)的html网页,提取了图片以及标 ...

  9. Android开发中无处不在的设计模式——动态代理模式

    继续更新设计模式系列.写这个模式的主要原因是近期看到了动态代理的代码. 先来回想一下前5个模式: - Android开发中无处不在的设计模式--单例模式 - Android开发中无处不在的设计模式-- ...

随机推荐

  1. Xamarin.Android之动画

    Translate动画 这个动画是最常使用到的,主要就是将控件从一个位置移动到另一个位置,并且还可以在这其中增加一定的效果,下面我们将采用两种方式实现动画,首选的是利用XML来制作动画,其次就是利用代 ...

  2. 关于mvc中@Html.DropDownListFor和@Html.DropDownList默认值无法选中问题简单总结

    当我们在做类似编辑功能的时候,会给定select选中默认值,然而mvc中偶尔这个功能不能用,或者是强类型的@Html.DropDownListFor不能用.凑巧今天遇到问题,解决问题时发现了mvc的一 ...

  3. OWIN规范中最让人费解的地方

    OWIN defines a standard interface between .NET web servers and web applications. OWIN最让人费解不是OWIN的五大角 ...

  4. [MSSQL2012]First_Value函数

    First_Value返回结果集中某列第一条数据的值,跟TOP 1效果一样,比较简单的一个函数 先贴测试用代码 DECLARE @TestData TABLE(     ID INT IDENTITY ...

  5. Twitter API升级至1.1

    Twitter API 1.1是至今最大的一次升级,从3月份提出,到6月11日1.0版本已经全面停止调用.关于1.1版本升级特性可访问: https://dev.twitter.com/docs/ap ...

  6. java5 CountDownLatch同步工具

    好像倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1,当到达0时,所有等待者就开始执行. java.util.concurrent.CountDownLatch ...

  7. Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

    介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间, ...

  8. atitit. 统计功能框架的最佳实践(1)---- on hibernate criteria

    atitit. 统计功能框架的最佳实践(1)---- on hibernate criteria 1. 关键字 1 2. 统计功能框架普通有有些条件选项...一个日期选项..一个日期类型(日,周,月份 ...

  9. 教你轻松看懂 iOS9 新功能

    2015苹果全球开发者大会在6月9日凌晨,美国旧金山举行,fir.im 整理了一部分的资料,帮助了解 iOS9 的新特性与功能,感兴趣的可以看下. 关于iOS9新增功能 在WWDC 2015上苹果介绍 ...

  10. iOS数据持久化-OC

    沙盒详解 1.IOS沙盒机制 IOS应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒,所以所有的非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文 ...